push 6e61d6ca5bcaf95ac09a664b4ba4f88238c927be
[wine/hacks.git] / dlls / winex11.drv / xrender.c
blob27e495ba108b483af917d2946b6505beb5010b89
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 are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict.
1856 * The 'src_pict' data effectively acts as an alpha channel to the tile data. We need PictOpOver for correct rendering. */
1857 int op = mask_pict ? PictOpOver : PictOpSrc;
1859 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1860 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1861 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1862 if(xscale != 1.0 || yscale != 1.0)
1864 if(mask_pict)
1865 set_xrender_transformation(mask_pict, xscale, yscale, x_src + x_offset, y_src + y_offset);
1866 else
1867 set_xrender_transformation(src_pict, xscale, yscale, x_src + x_offset, y_src + y_offset);
1869 pXRenderComposite(gdi_display, op, src_pict, mask_pict, dst_pict, 0, 0, 0, 0, 0, 0, width, height);
1871 else
1873 if(mask_pict)
1875 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
1876 /* Note since the 'source data' is in the mask picture, we have to pass x_src / y_src using mask_x / mask_y */
1877 pXRenderComposite(gdi_display, op, src_pict, mask_pict, dst_pict, 0, 0, x_src, y_src, 0, 0, width, height);
1879 else
1881 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1882 pXRenderComposite(gdi_display, op, src_pict, mask_pict, dst_pict, x_src, y_src, 0, 0, 0, 0, width, height);
1887 /******************************************************************************
1888 * AlphaBlend (x11drv.@)
1890 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1891 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1892 BLENDFUNCTION blendfn)
1894 XRenderPictureAttributes pa;
1895 Picture dst_pict, src_pict;
1896 Pixmap xpm;
1897 DIBSECTION dib;
1898 XImage *image;
1899 GC gc;
1900 XGCValues gcv;
1901 DWORD *dstbits, *data;
1902 int y, y2;
1903 POINT pts[2];
1904 BOOL top_down = FALSE;
1905 const WineXRenderFormat *src_format;
1906 int repeat_src;
1908 if(!X11DRV_XRender_Installed) {
1909 FIXME("Unable to AlphaBlend without Xrender\n");
1910 return FALSE;
1912 pts[0].x = xDst;
1913 pts[0].y = yDst;
1914 pts[1].x = xDst + widthDst;
1915 pts[1].y = yDst + heightDst;
1916 LPtoDP(devDst->hdc, pts, 2);
1917 xDst = pts[0].x;
1918 yDst = pts[0].y;
1919 widthDst = pts[1].x - pts[0].x;
1920 heightDst = pts[1].y - pts[0].y;
1922 pts[0].x = xSrc;
1923 pts[0].y = ySrc;
1924 pts[1].x = xSrc + widthSrc;
1925 pts[1].y = ySrc + heightSrc;
1926 LPtoDP(devSrc->hdc, pts, 2);
1927 xSrc = pts[0].x;
1928 ySrc = pts[0].y;
1929 widthSrc = pts[1].x - pts[0].x;
1930 heightSrc = pts[1].y - pts[0].y;
1931 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1933 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1934 if(widthDst != widthSrc || heightDst != heightSrc)
1935 #else
1936 if(!pXRenderSetPictureTransform)
1937 #endif
1939 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1940 return FALSE;
1943 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1945 static BOOL out = FALSE;
1946 if (!out)
1948 FIXME("not a dibsection\n");
1949 out = TRUE;
1951 return FALSE;
1954 /* If the source is a 1x1 bitmap, tiling is equivalent to stretching, but
1955 tiling is much faster. Therefore, we do no stretching in this case. */
1956 repeat_src = dib.dsBmih.biWidth == 1 && dib.dsBmih.biHeight == 1;
1958 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1959 || ySrc + heightSrc > dib.dsBmih.biHeight)
1961 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1962 SetLastError(ERROR_INVALID_PARAMETER);
1963 return FALSE;
1966 if(dib.dsBm.bmBitsPixel != 32) {
1967 FIXME("not a 32 bpp dibsection\n");
1968 return FALSE;
1970 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1972 if (devSrc->bitmap->topdown) { /* top-down dib */
1973 top_down = TRUE;
1974 dstbits += widthSrc * (heightSrc - 1);
1975 y2 = ySrc;
1976 y = y2 + heightSrc - 1;
1978 else
1980 y = dib.dsBmih.biHeight - ySrc - 1;
1981 y2 = y - heightSrc + 1;
1984 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1986 if (blendfn.SourceConstantAlpha == 0xff)
1988 for (; y >= y2; y--)
1990 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1991 widthSrc * 4);
1992 dstbits += (top_down ? -1 : 1) * widthSrc;
1995 else
1997 /* SourceConstantAlpha combined with source alpha */
1998 for (; y >= y2; y--)
2000 int x;
2001 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
2002 for (x = 0; x < widthSrc; x++)
2004 DWORD argb = *srcbits++;
2005 BYTE *s = (BYTE *) &argb;
2006 s[0] = (s[0] * blendfn.SourceConstantAlpha) / 255;
2007 s[1] = (s[1] * blendfn.SourceConstantAlpha) / 255;
2008 s[2] = (s[2] * blendfn.SourceConstantAlpha) / 255;
2009 s[3] = (s[3] * blendfn.SourceConstantAlpha) / 255;
2010 *dstbits++ = argb;
2012 if (top_down) /* we traversed the row forward so we should go back by two rows */
2013 dstbits -= 2 * widthSrc;
2017 else
2019 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
2020 int x;
2022 for(; y >= y2; y--)
2024 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
2025 for (x = 0; x < widthSrc; x++)
2027 DWORD argb = *srcbits++;
2028 argb = (argb & 0xffffff) | source_alpha;
2029 *dstbits++ = argb;
2031 if (top_down) /* we traversed the row forward so we should go back by two rows */
2032 dstbits -= 2 * widthSrc;
2037 dst_pict = get_xrender_picture(devDst);
2039 wine_tsx11_lock();
2040 src_format = get_xrender_format(WXR_FORMAT_A8R8G8B8);
2041 TRACE("src_format %p\n", src_format);
2042 if(!src_format)
2044 WARN("Unable to find a picture format supporting alpha, make sure X is running at 24-bit\n");
2045 wine_tsx11_unlock();
2046 HeapFree(GetProcessHeap(), 0, data);
2047 return FALSE;
2050 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
2051 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
2053 TRACE("src_drawable = %08lx\n", devSrc->drawable);
2054 xpm = XCreatePixmap(gdi_display,
2055 root_window,
2056 widthSrc, heightSrc, 32);
2057 gcv.graphics_exposures = False;
2058 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
2059 TRACE("xpm = %08lx\n", xpm);
2060 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
2062 pa.subwindow_mode = IncludeInferiors;
2063 pa.repeat = repeat_src ? RepeatNormal : RepeatNone;
2064 src_pict = pXRenderCreatePicture(gdi_display,
2065 xpm, src_format->pict_format,
2066 CPSubwindowMode|CPRepeat, &pa);
2067 TRACE("src_pict %08lx\n", src_pict);
2069 /* Make sure we ALWAYS set the transformation matrix even if we don't need to scale. The reason is
2070 * that later on we want to reuse pictures (it can bring a lot of extra performance) and each time
2071 * a different transformation matrix might have been used. */
2072 if (repeat_src)
2073 set_xrender_transformation(src_pict, 1.0, 1.0, 0, 0);
2074 else
2075 set_xrender_transformation(src_pict, widthSrc/(double)widthDst, heightSrc/(double)heightDst, 0, 0);
2076 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
2077 0, 0, 0, 0,
2078 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
2081 pXRenderFreePicture(gdi_display, src_pict);
2082 XFreePixmap(gdi_display, xpm);
2083 XFreeGC(gdi_display, gc);
2084 image->data = NULL;
2085 XDestroyImage(image);
2087 wine_tsx11_unlock();
2088 HeapFree(GetProcessHeap(), 0, data);
2089 return TRUE;
2092 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2094 /* 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 */
2095 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2096 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2097 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2099 wine_tsx11_lock();
2100 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2102 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2103 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2104 (src_format->format == dst_format->format) )
2106 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2107 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2109 else /* We need depth conversion */
2111 Picture src_pict, dst_pict;
2112 XRenderPictureAttributes pa;
2113 pa.subwindow_mode = IncludeInferiors;
2114 pa.repeat = RepeatNone;
2116 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2117 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2119 xrender_blit(src_pict, 0, dst_pict, 0, 0, 1.0, 1.0, width, height);
2120 pXRenderFreePicture(gdi_display, src_pict);
2121 pXRenderFreePicture(gdi_display, dst_pict);
2123 wine_tsx11_unlock();
2126 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2127 Pixmap pixmap, GC gc,
2128 INT widthSrc, INT heightSrc,
2129 INT widthDst, INT heightDst,
2130 RECT *visRectSrc, RECT *visRectDst )
2132 BOOL stretch = (widthSrc != widthDst) || (heightSrc != heightDst);
2133 int width = visRectDst->right - visRectDst->left;
2134 int height = visRectDst->bottom - visRectDst->top;
2135 int x_src = physDevSrc->dc_rect.left + visRectSrc->left;
2136 int y_src = physDevSrc->dc_rect.top + visRectSrc->top;
2137 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2138 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2139 Picture src_pict=0, dst_pict=0, mask_pict=0;
2141 double xscale = widthSrc/(double)widthDst;
2142 double yscale = heightSrc/(double)heightDst;
2144 XRenderPictureAttributes pa;
2145 pa.subwindow_mode = IncludeInferiors;
2146 pa.repeat = RepeatNone;
2148 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n", physDevSrc->depth, widthSrc, heightSrc, x_src, y_src);
2149 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, widthDst, heightDst);
2151 if(!X11DRV_XRender_Installed)
2153 TRACE("Not using XRender since it is not available or disabled\n");
2154 return FALSE;
2157 /* XRender can't handle palettes, so abort */
2158 if(X11DRV_PALETTE_XPixelToPalette)
2159 return FALSE;
2161 /* XRender is of no use in this case */
2162 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2163 return FALSE;
2165 /* Just use traditional X copy when the formats match and we don't need stretching */
2166 if((src_info->format->format == dst_format->format) && !stretch)
2168 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2169 wine_tsx11_lock();
2170 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2171 wine_tsx11_unlock();
2172 return TRUE;
2175 /* mono -> color */
2176 if(physDevSrc->depth == 1 && physDevDst->depth > 1)
2178 XRenderColor col;
2179 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2181 /* We use the source drawable as a mask */
2182 mask_pict = get_xrender_picture_source(physDevSrc);
2184 /* Use backgroundPixel as the foreground color */
2185 EnterCriticalSection( &xrender_cs );
2186 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2188 /* Create a destination picture and fill it with textPixel color as the background color */
2189 wine_tsx11_lock();
2190 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2191 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2193 xrender_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2195 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2196 wine_tsx11_unlock();
2197 LeaveCriticalSection( &xrender_cs );
2199 else /* color -> color (can be at different depths) or mono -> mono */
2201 src_pict = get_xrender_picture_source(physDevSrc);
2203 wine_tsx11_lock();
2204 dst_pict = pXRenderCreatePicture(gdi_display,
2205 pixmap, dst_format->pict_format,
2206 CPSubwindowMode|CPRepeat, &pa);
2208 xrender_blit(src_pict, 0, dst_pict, x_src, y_src, xscale, yscale, width, height);
2210 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2211 wine_tsx11_unlock();
2213 return TRUE;
2216 #else /* SONAME_LIBXRENDER */
2218 void X11DRV_XRender_Init(void)
2220 TRACE("XRender support not compiled in.\n");
2221 return;
2224 void X11DRV_XRender_Finalize(void)
2228 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2230 assert(0);
2231 return FALSE;
2234 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2236 assert(0);
2237 return;
2240 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2242 assert(0);
2243 return;
2246 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2247 const RECT *lprect, LPCWSTR wstr, UINT count,
2248 const INT *lpDx )
2250 assert(0);
2251 return FALSE;
2254 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2256 assert(0);
2257 return;
2260 /******************************************************************************
2261 * AlphaBlend (x11drv.@)
2263 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
2264 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
2265 BLENDFUNCTION blendfn)
2267 FIXME("not supported - XRENDER headers were missing at compile time\n");
2268 return FALSE;
2271 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2273 wine_tsx11_lock();
2274 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2276 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2277 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2278 wine_tsx11_unlock();
2281 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, const DIBSECTION *dib)
2283 return FALSE;
2286 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2287 Pixmap pixmap, GC gc,
2288 INT widthSrc, INT heightSrc,
2289 INT widthDst, INT heightDst,
2290 RECT *visRectSrc, RECT *visRectDst )
2292 return FALSE;
2294 #endif /* SONAME_LIBXRENDER */