push 337eb2e2d902d84a5d689451984c5832d7e04fc4
[wine/hacks.git] / dlls / winex11.drv / xrender.c
blob3bad0a3bc99645b92e20849fe5aaf16c2cb45506
1 /*
2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
6 * Some parts also:
7 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include "wine/port.h"
26 #include <assert.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "x11drv.h"
34 #include "winternl.h"
35 #include "wine/library.h"
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
39 int using_client_side_fonts = FALSE;
41 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
43 #ifdef SONAME_LIBXRENDER
45 static BOOL X11DRV_XRender_Installed = FALSE;
47 #include <X11/Xlib.h>
48 #include <X11/extensions/Xrender.h>
51 enum drawable_depth_type {mono_drawable, color_drawable};
52 static XRenderPictFormat *pict_formats[2];
54 typedef struct
56 LOGFONTW lf;
57 SIZE devsize; /* size in device coords */
58 DWORD hash;
59 } LFANDSIZE;
61 #define INITIAL_REALIZED_BUF_SIZE 128
63 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
65 typedef struct
67 GlyphSet glyphset;
68 XRenderPictFormat *font_format;
69 int nrealized;
70 BOOL *realized;
71 void **bitmaps;
72 XGlyphInfo *gis;
73 } gsCacheEntryFormat;
75 typedef struct
77 LFANDSIZE lfsz;
78 AA_Type aa_default;
79 gsCacheEntryFormat * format[AA_MAXVALUE];
80 INT count;
81 INT next;
82 } gsCacheEntry;
84 struct tagXRENDERINFO
86 int cache_index;
87 Picture pict;
91 static gsCacheEntry *glyphsetCache = NULL;
92 static DWORD glyphsetCacheSize = 0;
93 static INT lastfree = -1;
94 static INT mru = -1;
96 #define INIT_CACHE_SIZE 10
98 static int antialias = 1;
100 static void *xrender_handle;
102 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
103 MAKE_FUNCPTR(XRenderAddGlyphs)
104 MAKE_FUNCPTR(XRenderComposite)
105 MAKE_FUNCPTR(XRenderCompositeString8)
106 MAKE_FUNCPTR(XRenderCompositeString16)
107 MAKE_FUNCPTR(XRenderCompositeString32)
108 MAKE_FUNCPTR(XRenderCompositeText16)
109 MAKE_FUNCPTR(XRenderCreateGlyphSet)
110 MAKE_FUNCPTR(XRenderCreatePicture)
111 MAKE_FUNCPTR(XRenderFillRectangle)
112 MAKE_FUNCPTR(XRenderFindFormat)
113 MAKE_FUNCPTR(XRenderFindVisualFormat)
114 MAKE_FUNCPTR(XRenderFreeGlyphSet)
115 MAKE_FUNCPTR(XRenderFreePicture)
116 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
117 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
118 MAKE_FUNCPTR(XRenderSetPictureTransform)
119 #endif
120 MAKE_FUNCPTR(XRenderQueryExtension)
121 #undef MAKE_FUNCPTR
123 static CRITICAL_SECTION xrender_cs;
124 static CRITICAL_SECTION_DEBUG critsect_debug =
126 0, 0, &xrender_cs,
127 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
128 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
130 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
132 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
133 ( ( (ULONG)_x4 << 24 ) | \
134 ( (ULONG)_x3 << 16 ) | \
135 ( (ULONG)_x2 << 8 ) | \
136 (ULONG)_x1 )
138 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
140 #define GASP_GRIDFIT 0x01
141 #define GASP_DOGRAY 0x02
143 #ifdef WORDS_BIGENDIAN
144 #define get_be_word(x) (x)
145 #else
146 #define get_be_word(x) RtlUshortByteSwap(x)
147 #endif
149 /***********************************************************************
150 * X11DRV_XRender_Init
152 * Let's see if our XServer has the extension available
155 void X11DRV_XRender_Init(void)
157 int event_base, i;
158 XRenderPictFormat pf;
160 if (client_side_with_render &&
161 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
162 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
163 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
166 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
167 LOAD_FUNCPTR(XRenderAddGlyphs)
168 LOAD_FUNCPTR(XRenderComposite)
169 LOAD_FUNCPTR(XRenderCompositeString8)
170 LOAD_FUNCPTR(XRenderCompositeString16)
171 LOAD_FUNCPTR(XRenderCompositeString32)
172 LOAD_FUNCPTR(XRenderCompositeText16)
173 LOAD_FUNCPTR(XRenderCreateGlyphSet)
174 LOAD_FUNCPTR(XRenderCreatePicture)
175 LOAD_FUNCPTR(XRenderFillRectangle)
176 LOAD_FUNCPTR(XRenderFindFormat)
177 LOAD_FUNCPTR(XRenderFindVisualFormat)
178 LOAD_FUNCPTR(XRenderFreeGlyphSet)
179 LOAD_FUNCPTR(XRenderFreePicture)
180 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
181 LOAD_FUNCPTR(XRenderQueryExtension)
182 #undef LOAD_FUNCPTR
183 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
184 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
185 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
186 #undef LOAD_OPTIONAL_FUNCPTR
187 #endif
190 wine_tsx11_lock();
191 if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
192 X11DRV_XRender_Installed = TRUE;
193 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
194 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
195 if(!pict_formats[color_drawable])
197 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
198 if (visual->class == DirectColor)
200 XVisualInfo info;
201 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
202 screen_depth, TrueColor, &info ))
204 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
205 if (pict_formats[color_drawable]) visual = info.visual;
209 if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
211 wine_tsx11_unlock();
212 WINE_MESSAGE(
213 "Wine has detected that you probably have a buggy version\n"
214 "of libXrender.so . Because of this client side font rendering\n"
215 "will be disabled. Please upgrade this library.\n");
216 X11DRV_XRender_Installed = FALSE;
217 return;
219 pf.type = PictTypeDirect;
220 pf.depth = 1;
221 pf.direct.alpha = 0;
222 pf.direct.alphaMask = 1;
223 pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
224 PictFormatDepth | PictFormatAlpha |
225 PictFormatAlphaMask, &pf, 0);
226 if(!pict_formats[mono_drawable]) {
227 ERR("mono_format == NULL?\n");
228 X11DRV_XRender_Installed = FALSE;
230 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
231 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
232 X11DRV_XRender_Installed = FALSE;
235 wine_tsx11_unlock();
238 sym_not_found:
239 if(X11DRV_XRender_Installed || client_side_with_core)
241 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
242 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
244 glyphsetCacheSize = INIT_CACHE_SIZE;
245 lastfree = 0;
246 for(i = 0; i < INIT_CACHE_SIZE; i++) {
247 glyphsetCache[i].next = i + 1;
248 glyphsetCache[i].count = -1;
250 glyphsetCache[i-1].next = -1;
251 using_client_side_fonts = 1;
253 if(!X11DRV_XRender_Installed) {
254 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
255 if(screen_depth <= 8 || !client_side_antialias_with_core)
256 antialias = 0;
257 } else {
258 if(screen_depth <= 8 || !client_side_antialias_with_render)
259 antialias = 0;
262 else TRACE("Using X11 core fonts\n");
265 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
267 if(p1->hash != p2->hash) return TRUE;
268 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
269 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
270 return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
273 #if 0
274 static void walk_cache(void)
276 int i;
278 EnterCriticalSection(&xrender_cs);
279 for(i=mru; i >= 0; i = glyphsetCache[i].next)
280 TRACE("item %d\n", i);
281 LeaveCriticalSection(&xrender_cs);
283 #endif
285 static int LookupEntry(LFANDSIZE *plfsz)
287 int i, prev_i = -1;
289 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
290 TRACE("%d\n", i);
291 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
292 i = -1;
293 break;
296 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
297 glyphsetCache[i].count++;
298 if(prev_i >= 0) {
299 glyphsetCache[prev_i].next = glyphsetCache[i].next;
300 glyphsetCache[i].next = mru;
301 mru = i;
303 TRACE("found font in cache %d\n", i);
304 return i;
306 prev_i = i;
308 TRACE("font not in cache\n");
309 return -1;
312 static void FreeEntry(int entry)
314 int i, format;
316 for(format = 0; format < AA_MAXVALUE; format++) {
317 gsCacheEntryFormat * formatEntry;
319 if( !glyphsetCache[entry].format[format] )
320 continue;
322 formatEntry = glyphsetCache[entry].format[format];
324 if(formatEntry->glyphset) {
325 wine_tsx11_lock();
326 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
327 wine_tsx11_unlock();
328 formatEntry->glyphset = 0;
330 if(formatEntry->nrealized) {
331 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
332 formatEntry->realized = NULL;
333 if(formatEntry->bitmaps) {
334 for(i = 0; i < formatEntry->nrealized; i++)
335 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
336 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
337 formatEntry->bitmaps = NULL;
339 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
340 formatEntry->gis = NULL;
341 formatEntry->nrealized = 0;
344 HeapFree(GetProcessHeap(), 0, formatEntry);
345 glyphsetCache[entry].format[format] = NULL;
349 static int AllocEntry(void)
351 int best = -1, prev_best = -1, i, prev_i = -1;
353 if(lastfree >= 0) {
354 assert(glyphsetCache[lastfree].count == -1);
355 glyphsetCache[lastfree].count = 1;
356 best = lastfree;
357 lastfree = glyphsetCache[lastfree].next;
358 assert(best != mru);
359 glyphsetCache[best].next = mru;
360 mru = best;
362 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
363 return mru;
366 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
367 if(glyphsetCache[i].count == 0) {
368 best = i;
369 prev_best = prev_i;
371 prev_i = i;
374 if(best >= 0) {
375 TRACE("freeing unused glyphset at cache %d\n", best);
376 FreeEntry(best);
377 glyphsetCache[best].count = 1;
378 if(prev_best >= 0) {
379 glyphsetCache[prev_best].next = glyphsetCache[best].next;
380 glyphsetCache[best].next = mru;
381 mru = best;
382 } else {
383 assert(mru == best);
385 return mru;
388 TRACE("Growing cache\n");
390 if (glyphsetCache)
391 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
392 glyphsetCache,
393 (glyphsetCacheSize + INIT_CACHE_SIZE)
394 * sizeof(*glyphsetCache));
395 else
396 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
397 (glyphsetCacheSize + INIT_CACHE_SIZE)
398 * sizeof(*glyphsetCache));
400 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
401 i++) {
402 glyphsetCache[i].next = i + 1;
403 glyphsetCache[i].count = -1;
405 glyphsetCache[i-1].next = -1;
406 glyphsetCacheSize += INIT_CACHE_SIZE;
408 lastfree = glyphsetCache[best].next;
409 glyphsetCache[best].count = 1;
410 glyphsetCache[best].next = mru;
411 mru = best;
412 TRACE("new free cache slot at %d\n", mru);
413 return mru;
416 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
418 DWORD size;
419 WORD *gasp, *buffer;
420 WORD num_recs;
421 DWORD ppem;
422 TEXTMETRICW tm;
424 *flags = 0;
426 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
427 if(size == GDI_ERROR)
428 return FALSE;
430 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
431 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
433 GetTextMetricsW(physDev->hdc, &tm);
434 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
436 gasp++;
437 num_recs = get_be_word(*gasp);
438 gasp++;
439 while(num_recs--)
441 *flags = get_be_word(*(gasp + 1));
442 if(ppem <= get_be_word(*gasp))
443 break;
444 gasp += 2;
446 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
448 HeapFree(GetProcessHeap(), 0, buffer);
449 return TRUE;
452 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
454 int ret;
455 int format;
456 gsCacheEntry *entry;
457 WORD flags;
458 static int hinter = -1;
460 if((ret = LookupEntry(plfsz)) != -1) return ret;
462 ret = AllocEntry();
463 entry = glyphsetCache + ret;
464 entry->lfsz = *plfsz;
465 for( format = 0; format < AA_MAXVALUE; format++ ) {
466 assert( !entry->format[format] );
469 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
471 if(hinter == -1)
473 RASTERIZER_STATUS status;
474 GetRasterizerCaps(&status, sizeof(status));
475 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
477 if(!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
478 entry->aa_default = AA_Grey;
479 else
480 entry->aa_default = AA_None;
482 else
483 entry->aa_default = AA_None;
485 return ret;
488 static void dec_ref_cache(int index)
490 assert(index >= 0);
491 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
492 assert(glyphsetCache[index].count > 0);
493 glyphsetCache[index].count--;
496 static void lfsz_calc_hash(LFANDSIZE *plfsz)
498 DWORD hash = 0, *ptr;
499 int i;
501 hash ^= plfsz->devsize.cx;
502 hash ^= plfsz->devsize.cy;
503 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
504 hash ^= *ptr;
505 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
506 WCHAR *pwc = (WCHAR *)ptr;
507 if(!*pwc) break;
508 hash ^= *ptr;
509 pwc++;
510 if(!*pwc) break;
512 plfsz->hash = hash;
513 return;
516 /***********************************************************************
517 * X11DRV_XRender_Finalize
519 void X11DRV_XRender_Finalize(void)
521 int i;
523 EnterCriticalSection(&xrender_cs);
524 for(i = mru; i >= 0; i = glyphsetCache[i].next)
525 FreeEntry(i);
526 LeaveCriticalSection(&xrender_cs);
530 /***********************************************************************
531 * X11DRV_XRender_SelectFont
533 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
535 LFANDSIZE lfsz;
537 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
538 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
539 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
540 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
541 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
542 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
543 lfsz_calc_hash(&lfsz);
545 EnterCriticalSection(&xrender_cs);
546 if(!physDev->xrender) {
547 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
548 sizeof(*physDev->xrender));
549 physDev->xrender->cache_index = -1;
551 else if(physDev->xrender->cache_index != -1)
552 dec_ref_cache(physDev->xrender->cache_index);
553 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
554 LeaveCriticalSection(&xrender_cs);
555 return 0;
558 /***********************************************************************
559 * X11DRV_XRender_DeleteDC
561 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
563 X11DRV_XRender_UpdateDrawable(physDev);
565 EnterCriticalSection(&xrender_cs);
566 if(physDev->xrender->cache_index != -1)
567 dec_ref_cache(physDev->xrender->cache_index);
568 LeaveCriticalSection(&xrender_cs);
570 HeapFree(GetProcessHeap(), 0, physDev->xrender);
571 physDev->xrender = NULL;
572 return;
575 /***********************************************************************
576 * X11DRV_XRender_UpdateDrawable
578 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
579 * It deletes the pict and tile when the drawable changes.
581 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
583 wine_tsx11_lock();
585 if(physDev->xrender->pict)
587 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
588 XFlush(gdi_display);
589 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
590 physDev->xrender->pict = 0;
592 wine_tsx11_unlock();
594 return;
597 /************************************************************************
598 * UploadGlyph
600 * Helper to ExtTextOut. Must be called inside xrender_cs
602 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
604 unsigned int buflen;
605 char *buf;
606 Glyph gid;
607 GLYPHMETRICS gm;
608 XGlyphInfo gi;
609 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
610 gsCacheEntryFormat *formatEntry;
611 UINT ggo_format = GGO_GLYPH_INDEX;
612 XRenderPictFormat pf;
613 static const char zero[4];
615 switch(format) {
616 case AA_Grey:
617 ggo_format |= WINE_GGO_GRAY16_BITMAP;
618 break;
620 default:
621 ERR("aa = %d - not implemented\n", format);
622 case AA_None:
623 ggo_format |= GGO_BITMAP;
624 break;
627 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
628 NULL);
629 if(buflen == GDI_ERROR) {
630 if(format != AA_None) {
631 format = AA_None;
632 entry->aa_default = AA_None;
633 ggo_format &= ~WINE_GGO_GRAY16_BITMAP;
634 ggo_format |= GGO_BITMAP;
635 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
636 NULL);
638 if(buflen == GDI_ERROR) {
639 WARN("GetGlyphOutlineW failed\n");
640 return FALSE;
642 TRACE("Turning off antialiasing for this monochrome font\n");
645 /* If there is nothing for the current type, we create the entry. */
646 if( !entry->format[format] ) {
647 entry->format[format] = HeapAlloc(GetProcessHeap(),
648 HEAP_ZERO_MEMORY,
649 sizeof(gsCacheEntryFormat));
651 formatEntry = entry->format[format];
653 if(formatEntry->nrealized <= glyph) {
654 formatEntry->nrealized = (glyph / 128 + 1) * 128;
656 if (formatEntry->realized)
657 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
658 HEAP_ZERO_MEMORY,
659 formatEntry->realized,
660 formatEntry->nrealized * sizeof(BOOL));
661 else
662 formatEntry->realized = HeapAlloc(GetProcessHeap(),
663 HEAP_ZERO_MEMORY,
664 formatEntry->nrealized * sizeof(BOOL));
666 if(!X11DRV_XRender_Installed) {
667 if (formatEntry->bitmaps)
668 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
669 HEAP_ZERO_MEMORY,
670 formatEntry->bitmaps,
671 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
672 else
673 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
674 HEAP_ZERO_MEMORY,
675 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
677 if (formatEntry->gis)
678 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
679 HEAP_ZERO_MEMORY,
680 formatEntry->gis,
681 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
682 else
683 formatEntry->gis = HeapAlloc(GetProcessHeap(),
684 HEAP_ZERO_MEMORY,
685 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
689 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
690 switch(format) {
691 case AA_Grey:
692 pf.depth = 8;
693 pf.direct.alphaMask = 0xff;
694 break;
696 default:
697 ERR("aa = %d - not implemented\n", format);
698 case AA_None:
699 pf.depth = 1;
700 pf.direct.alphaMask = 1;
701 break;
704 pf.type = PictTypeDirect;
705 pf.direct.alpha = 0;
707 wine_tsx11_lock();
708 formatEntry->font_format = pXRenderFindFormat(gdi_display,
709 PictFormatType |
710 PictFormatDepth |
711 PictFormatAlpha |
712 PictFormatAlphaMask,
713 &pf, 0);
715 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
716 wine_tsx11_unlock();
720 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
721 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, NULL);
722 formatEntry->realized[glyph] = TRUE;
724 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
725 buflen,
726 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
727 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
729 gi.width = gm.gmBlackBoxX;
730 gi.height = gm.gmBlackBoxY;
731 gi.x = -gm.gmptGlyphOrigin.x;
732 gi.y = gm.gmptGlyphOrigin.y;
733 gi.xOff = gm.gmCellIncX;
734 gi.yOff = gm.gmCellIncY;
736 if(TRACE_ON(xrender)) {
737 int pitch, i, j;
738 char output[300];
739 unsigned char *line;
741 if(format == AA_None) {
742 pitch = ((gi.width + 31) / 32) * 4;
743 for(i = 0; i < gi.height; i++) {
744 line = (unsigned char*) buf + i * pitch;
745 output[0] = '\0';
746 for(j = 0; j < pitch * 8; j++) {
747 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
749 TRACE("%s\n", output);
751 } else {
752 static const char blks[] = " .:;!o*#";
753 char str[2];
755 str[1] = '\0';
756 pitch = ((gi.width + 3) / 4) * 4;
757 for(i = 0; i < gi.height; i++) {
758 line = (unsigned char*) buf + i * pitch;
759 output[0] = '\0';
760 for(j = 0; j < pitch; j++) {
761 str[0] = blks[line[j] >> 5];
762 strcat(output, str);
764 TRACE("%s\n", output);
770 if(formatEntry->glyphset) {
771 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
772 unsigned char *byte = (unsigned char*) buf, c;
773 int i = buflen;
775 while(i--) {
776 c = *byte;
778 /* magic to flip bit order */
779 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
780 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
781 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
783 *byte++ = c;
786 gid = glyph;
789 XRenderCompositeText seems to ignore 0x0 glyphs when
790 AA_None, which means we lose the advance width of glyphs
791 like the space. We'll pretend that such glyphs are 1x1
792 bitmaps.
795 if(buflen == 0)
796 gi.width = gi.height = 1;
798 wine_tsx11_lock();
799 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
800 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
801 wine_tsx11_unlock();
802 HeapFree(GetProcessHeap(), 0, buf);
803 } else {
804 formatEntry->bitmaps[glyph] = buf;
807 formatEntry->gis[glyph] = gi;
809 return TRUE;
812 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
813 void *bitmap, XGlyphInfo *gi)
815 unsigned char *srcLine = bitmap, *src;
816 unsigned char bits, bitsMask;
817 int width = gi->width;
818 int stride = ((width + 31) & ~31) >> 3;
819 int height = gi->height;
820 int w;
821 int xspan, lenspan;
823 TRACE("%d, %d\n", x, y);
824 x -= gi->x;
825 y -= gi->y;
826 while (height--)
828 src = srcLine;
829 srcLine += stride;
830 w = width;
832 bitsMask = 0x80; /* FreeType is always MSB first */
833 bits = *src++;
835 xspan = x;
836 while (w)
838 if (bits & bitsMask)
840 lenspan = 0;
843 lenspan++;
844 if (lenspan == w)
845 break;
846 bitsMask = bitsMask >> 1;
847 if (!bitsMask)
849 bits = *src++;
850 bitsMask = 0x80;
852 } while (bits & bitsMask);
853 XFillRectangle (gdi_display, physDev->drawable,
854 physDev->gc, xspan, y, lenspan, 1);
855 xspan += lenspan;
856 w -= lenspan;
858 else
862 w--;
863 xspan++;
864 if (!w)
865 break;
866 bitsMask = bitsMask >> 1;
867 if (!bitsMask)
869 bits = *src++;
870 bitsMask = 0x80;
872 } while (!(bits & bitsMask));
875 y++;
879 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
880 void *bitmap, XGlyphInfo *gi)
882 unsigned char *srcLine = bitmap, *src, bits;
883 int width = gi->width;
884 int stride = ((width + 3) & ~3);
885 int height = gi->height;
886 int w;
887 int xspan, lenspan;
889 x -= gi->x;
890 y -= gi->y;
891 while (height--)
893 src = srcLine;
894 srcLine += stride;
895 w = width;
897 bits = *src++;
898 xspan = x;
899 while (w)
901 if (bits >= 0x80)
903 lenspan = 0;
906 lenspan++;
907 if (lenspan == w)
908 break;
909 bits = *src++;
910 } while (bits >= 0x80);
911 XFillRectangle (gdi_display, physDev->drawable,
912 physDev->gc, xspan, y, lenspan, 1);
913 xspan += lenspan;
914 w -= lenspan;
916 else
920 w--;
921 xspan++;
922 if (!w)
923 break;
924 bits = *src++;
925 } while (bits < 0x80);
928 y++;
933 static void ExamineBitfield (DWORD mask, int *shift, int *len)
935 int s, l;
937 s = 0;
938 while ((mask & 1) == 0)
940 mask >>= 1;
941 s++;
943 l = 0;
944 while ((mask & 1) == 1)
946 mask >>= 1;
947 l++;
949 *shift = s;
950 *len = l;
953 static DWORD GetField (DWORD pixel, int shift, int len)
955 pixel = pixel & (((1 << (len)) - 1) << shift);
956 pixel = pixel << (32 - (shift + len)) >> 24;
957 while (len < 8)
959 pixel |= (pixel >> len);
960 len <<= 1;
962 return pixel;
966 static DWORD PutField (DWORD pixel, int shift, int len)
968 shift = shift - (8 - len);
969 if (len <= 8)
970 pixel &= (((1 << len) - 1) << (8 - len));
971 if (shift < 0)
972 pixel >>= -shift;
973 else
974 pixel <<= shift;
975 return pixel;
978 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
979 int color)
981 int r_shift, r_len;
982 int g_shift, g_len;
983 int b_shift, b_len;
984 BYTE *maskLine, *mask, m;
985 int maskStride;
986 DWORD pixel;
987 int width, height;
988 int w, tx;
989 BYTE src_r, src_g, src_b;
991 x -= gi->x;
992 y -= gi->y;
993 width = gi->width;
994 height = gi->height;
996 maskLine = (unsigned char *) bitmap;
997 maskStride = (width + 3) & ~3;
999 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1000 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1001 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1003 src_r = GetField(color, r_shift, r_len);
1004 src_g = GetField(color, g_shift, g_len);
1005 src_b = GetField(color, b_shift, b_len);
1007 for(; height--; y++)
1009 mask = maskLine;
1010 maskLine += maskStride;
1011 w = width;
1012 tx = x;
1014 if(y < 0) continue;
1015 if(y >= image->height) break;
1017 for(; w--; tx++)
1019 if(tx >= image->width) break;
1021 m = *mask++;
1022 if(tx < 0) continue;
1024 if (m == 0xff)
1025 XPutPixel (image, tx, y, color);
1026 else if (m)
1028 BYTE r, g, b;
1030 pixel = XGetPixel (image, tx, y);
1032 r = GetField(pixel, r_shift, r_len);
1033 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1034 g = GetField(pixel, g_shift, g_len);
1035 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1036 b = GetField(pixel, b_shift, b_len);
1037 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1039 pixel = (PutField (r, r_shift, r_len) |
1040 PutField (g, g_shift, g_len) |
1041 PutField (b, b_shift, b_len));
1042 XPutPixel (image, tx, y, pixel);
1048 /*************************************************************
1049 * get_tile_pict
1051 * Returns an appropriate Picture for tiling the text colour.
1052 * Call and use result within the xrender_cs
1054 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1056 static struct
1058 Pixmap xpm;
1059 Picture pict;
1060 int current_color;
1061 } tiles[2], *tile;
1062 XRenderColor col;
1064 tile = &tiles[type];
1066 if(!tile->xpm)
1068 XRenderPictureAttributes pa;
1070 wine_tsx11_lock();
1071 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1073 pa.repeat = True;
1074 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1075 wine_tsx11_unlock();
1077 /* init current_color to something different from text_pixel */
1078 tile->current_color = ~text_pixel;
1080 if(type == mono_drawable)
1082 /* for a 1bpp bitmap we always need a 1 in the tile */
1083 col.red = col.green = col.blue = 0;
1084 col.alpha = 0xffff;
1085 wine_tsx11_lock();
1086 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1087 wine_tsx11_unlock();
1091 if(text_pixel != tile->current_color && type == color_drawable)
1093 /* Map 0 -- 0xff onto 0 -- 0xffff */
1094 int r_shift, r_len;
1095 int g_shift, g_len;
1096 int b_shift, b_len;
1098 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1099 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1100 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1102 col.red = GetField(text_pixel, r_shift, r_len);
1103 col.red |= col.red << 8;
1104 col.green = GetField(text_pixel, g_shift, g_len);
1105 col.green |= col.green << 8;
1106 col.blue = GetField(text_pixel, b_shift, b_len);
1107 col.blue |= col.blue << 8;
1108 col.alpha = 0xffff;
1110 wine_tsx11_lock();
1111 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1112 wine_tsx11_unlock();
1113 tile->current_color = text_pixel;
1115 return tile->pict;
1118 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1120 return 1;
1123 /***********************************************************************
1124 * X11DRV_XRender_ExtTextOut
1126 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1127 const RECT *lprect, LPCWSTR wstr, UINT count,
1128 const INT *lpDx )
1130 RGNDATA *data;
1131 XGCValues xgcval;
1132 gsCacheEntry *entry;
1133 gsCacheEntryFormat *formatEntry;
1134 BOOL retv = FALSE;
1135 HDC hdc = physDev->hdc;
1136 int textPixel, backgroundPixel;
1137 HRGN saved_region = 0;
1138 BOOL disable_antialias = FALSE;
1139 AA_Type aa_type = AA_None;
1140 DIBSECTION bmp;
1141 unsigned int idx;
1142 double cosEsc, sinEsc;
1143 LOGFONTW lf;
1144 enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1145 Picture tile_pict = 0;
1147 /* Do we need to disable antialiasing because of palette mode? */
1148 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1149 TRACE("bitmap is not a DIB\n");
1151 else if (bmp.dsBmih.biBitCount <= 8) {
1152 TRACE("Disabling antialiasing\n");
1153 disable_antialias = TRUE;
1156 xgcval.function = GXcopy;
1157 xgcval.background = physDev->backgroundPixel;
1158 xgcval.fill_style = FillSolid;
1159 wine_tsx11_lock();
1160 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1161 wine_tsx11_unlock();
1163 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1165 if(physDev->depth == 1) {
1166 if((physDev->textPixel & 0xffffff) == 0) {
1167 textPixel = 0;
1168 backgroundPixel = 1;
1169 } else {
1170 textPixel = 1;
1171 backgroundPixel = 0;
1173 } else {
1174 textPixel = physDev->textPixel;
1175 backgroundPixel = physDev->backgroundPixel;
1178 if(flags & ETO_OPAQUE)
1180 wine_tsx11_lock();
1181 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1182 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1183 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1184 lprect->right - lprect->left, lprect->bottom - lprect->top );
1185 wine_tsx11_unlock();
1188 if(count == 0)
1190 retv = TRUE;
1191 goto done_unlock;
1195 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1196 if(lf.lfEscapement != 0) {
1197 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1198 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1199 } else {
1200 cosEsc = 1;
1201 sinEsc = 0;
1204 if (flags & ETO_CLIPPED)
1206 HRGN clip_region;
1208 clip_region = CreateRectRgnIndirect( lprect );
1209 /* make a copy of the current device region */
1210 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1211 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1212 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1213 DeleteObject( clip_region );
1216 if(X11DRV_XRender_Installed) {
1217 if(!physDev->xrender->pict) {
1218 XRenderPictureAttributes pa;
1219 pa.subwindow_mode = IncludeInferiors;
1221 wine_tsx11_lock();
1222 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1223 physDev->drawable,
1224 pict_formats[depth_type],
1225 CPSubwindowMode, &pa);
1226 wine_tsx11_unlock();
1228 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1229 physDev->xrender->pict, hdc, physDev->drawable);
1230 } else {
1231 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1232 physDev->xrender->pict, hdc, physDev->drawable);
1235 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1237 wine_tsx11_lock();
1238 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1239 physDev->dc_rect.left, physDev->dc_rect.top,
1240 (XRectangle *)data->Buffer, data->rdh.nCount );
1241 wine_tsx11_unlock();
1242 HeapFree( GetProcessHeap(), 0, data );
1246 EnterCriticalSection(&xrender_cs);
1248 entry = glyphsetCache + physDev->xrender->cache_index;
1249 if( disable_antialias == FALSE )
1250 aa_type = entry->aa_default;
1251 formatEntry = entry->format[aa_type];
1253 for(idx = 0; idx < count; idx++) {
1254 if( !formatEntry ) {
1255 UploadGlyph(physDev, wstr[idx], aa_type);
1256 /* re-evaluate antialias since aa_default may have changed */
1257 if( disable_antialias == FALSE )
1258 aa_type = entry->aa_default;
1259 formatEntry = entry->format[aa_type];
1260 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1261 UploadGlyph(physDev, wstr[idx], aa_type);
1264 if (!formatEntry)
1266 WARN("could not upload requested glyphs\n");
1267 LeaveCriticalSection(&xrender_cs);
1268 goto done_unlock;
1271 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1272 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1274 if(X11DRV_XRender_Installed)
1276 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1277 INT offset = 0;
1278 POINT desired, current;
1279 int render_op = PictOpOver;
1281 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1282 So we pass zeros to the function and move to our starting position using the first
1283 element of the elts array. */
1285 desired.x = physDev->dc_rect.left + x;
1286 desired.y = physDev->dc_rect.top + y;
1287 current.x = current.y = 0;
1289 tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1291 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1293 if((depth_type == mono_drawable) && (textPixel == 0))
1294 render_op = PictOpOutReverse; /* This gives us 'black' text */
1296 for(idx = 0; idx < count; idx++)
1298 elts[idx].glyphset = formatEntry->glyphset;
1299 elts[idx].chars = wstr + idx;
1300 elts[idx].nchars = 1;
1301 elts[idx].xOff = desired.x - current.x;
1302 elts[idx].yOff = desired.y - current.y;
1304 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1305 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1307 if(!lpDx)
1309 desired.x += formatEntry->gis[wstr[idx]].xOff;
1310 desired.y += formatEntry->gis[wstr[idx]].yOff;
1312 else
1314 offset += lpDx[idx];
1315 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1316 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1319 wine_tsx11_lock();
1320 pXRenderCompositeText16(gdi_display, render_op,
1321 tile_pict,
1322 physDev->xrender->pict,
1323 formatEntry->font_format,
1324 0, 0, 0, 0, elts, count);
1325 wine_tsx11_unlock();
1326 HeapFree(GetProcessHeap(), 0, elts);
1327 } else {
1328 INT offset = 0, xoff = 0, yoff = 0;
1329 wine_tsx11_lock();
1330 XSetForeground( gdi_display, physDev->gc, textPixel );
1332 if(aa_type == AA_None || physDev->depth == 1)
1334 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1336 if(aa_type == AA_None)
1337 sharp_glyph_fn = SharpGlyphMono;
1338 else
1339 sharp_glyph_fn = SharpGlyphGray;
1341 for(idx = 0; idx < count; idx++) {
1342 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1343 physDev->dc_rect.top + y + yoff,
1344 formatEntry->bitmaps[wstr[idx]],
1345 &formatEntry->gis[wstr[idx]]);
1346 if(lpDx) {
1347 offset += lpDx[idx];
1348 xoff = offset * cosEsc;
1349 yoff = offset * -sinEsc;
1350 } else {
1351 xoff += formatEntry->gis[wstr[idx]].xOff;
1352 yoff += formatEntry->gis[wstr[idx]].yOff;
1355 } else {
1356 XImage *image;
1357 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1358 RECT extents = {0, 0, 0, 0};
1359 POINT cur = {0, 0};
1360 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1361 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1363 TRACE("drawable %dx%d\n", w, h);
1365 for(idx = 0; idx < count; idx++) {
1366 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1367 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1368 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1369 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1370 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1371 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1372 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1373 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1374 if(lpDx) {
1375 offset += lpDx[idx];
1376 cur.x = offset * cosEsc;
1377 cur.y = offset * -sinEsc;
1378 } else {
1379 cur.x += formatEntry->gis[wstr[idx]].xOff;
1380 cur.y += formatEntry->gis[wstr[idx]].yOff;
1383 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1384 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1386 if(physDev->dc_rect.left + x + extents.left >= 0) {
1387 image_x = physDev->dc_rect.left + x + extents.left;
1388 image_off_x = 0;
1389 } else {
1390 image_x = 0;
1391 image_off_x = physDev->dc_rect.left + x + extents.left;
1393 if(physDev->dc_rect.top + y + extents.top >= 0) {
1394 image_y = physDev->dc_rect.top + y + extents.top;
1395 image_off_y = 0;
1396 } else {
1397 image_y = 0;
1398 image_off_y = physDev->dc_rect.top + y + extents.top;
1400 if(physDev->dc_rect.left + x + extents.right < w)
1401 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1402 else
1403 image_w = w - image_x;
1404 if(physDev->dc_rect.top + y + extents.bottom < h)
1405 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1406 else
1407 image_h = h - image_y;
1409 if(image_w <= 0 || image_h <= 0) goto no_image;
1411 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1412 image = XGetImage(gdi_display, physDev->drawable,
1413 image_x, image_y, image_w, image_h,
1414 AllPlanes, ZPixmap);
1415 X11DRV_check_error();
1417 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1418 gdi_display, (int)physDev->drawable, image_x, image_y,
1419 image_w, image_h, AllPlanes, ZPixmap,
1420 physDev->depth, image);
1421 if(!image) {
1422 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1423 physDev->depth);
1424 GC gc;
1425 XGCValues gcv;
1427 gcv.graphics_exposures = False;
1428 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1429 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1430 image_w, image_h, 0, 0);
1431 XFreeGC(gdi_display, gc);
1432 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1433 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1434 ZPixmap);
1435 X11DRV_check_error();
1436 XFreePixmap(gdi_display, xpm);
1438 if(!image) goto no_image;
1440 image->red_mask = visual->red_mask;
1441 image->green_mask = visual->green_mask;
1442 image->blue_mask = visual->blue_mask;
1444 offset = xoff = yoff = 0;
1445 for(idx = 0; idx < count; idx++) {
1446 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1447 yoff + image_off_y - extents.top,
1448 formatEntry->bitmaps[wstr[idx]],
1449 &formatEntry->gis[wstr[idx]],
1450 physDev->textPixel);
1451 if(lpDx) {
1452 offset += lpDx[idx];
1453 xoff = offset * cosEsc;
1454 yoff = offset * -sinEsc;
1455 } else {
1456 xoff += formatEntry->gis[wstr[idx]].xOff;
1457 yoff += formatEntry->gis[wstr[idx]].yOff;
1460 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1461 image_x, image_y, image_w, image_h);
1462 XDestroyImage(image);
1464 no_image:
1465 wine_tsx11_unlock();
1467 LeaveCriticalSection(&xrender_cs);
1469 if (flags & ETO_CLIPPED)
1471 /* restore the device region */
1472 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1473 DeleteObject( saved_region );
1476 retv = TRUE;
1478 done_unlock:
1479 X11DRV_UnlockDIBSection( physDev, TRUE );
1480 return retv;
1483 /******************************************************************************
1484 * AlphaBlend (x11drv.@)
1486 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1487 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1488 BLENDFUNCTION blendfn)
1490 XRenderPictureAttributes pa;
1491 XRenderPictFormat *src_format;
1492 XRenderPictFormat argb32_templ = {
1493 0, /* id */
1494 PictTypeDirect, /* type */
1495 32, /* depth */
1496 { /* direct */
1497 16, /* direct.red */
1498 0xff, /* direct.redMask */
1499 8, /* direct.green */
1500 0xff, /* direct.greenMask */
1501 0, /* direct.blue */
1502 0xff, /* direct.blueMask */
1503 24, /* direct.alpha */
1504 0xff, /* direct.alphaMask */
1506 0, /* colormap */
1508 unsigned long argb32_templ_mask =
1509 PictFormatType |
1510 PictFormatDepth |
1511 PictFormatRed |
1512 PictFormatRedMask |
1513 PictFormatGreen |
1514 PictFormatGreenMask |
1515 PictFormatBlue |
1516 PictFormatBlueMask |
1517 PictFormatAlpha |
1518 PictFormatAlphaMask;
1520 Picture dst_pict, src_pict;
1521 Pixmap xpm;
1522 DIBSECTION dib;
1523 XImage *image;
1524 GC gc;
1525 XGCValues gcv;
1526 DWORD *dstbits, *data;
1527 int y, y2;
1528 POINT pts[2];
1529 BOOL top_down = FALSE;
1530 RGNDATA *rgndata;
1531 enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1533 if(!X11DRV_XRender_Installed) {
1534 FIXME("Unable to AlphaBlend without Xrender\n");
1535 return FALSE;
1537 pts[0].x = xDst;
1538 pts[0].y = yDst;
1539 pts[1].x = xDst + widthDst;
1540 pts[1].y = yDst + heightDst;
1541 LPtoDP(devDst->hdc, pts, 2);
1542 xDst = pts[0].x;
1543 yDst = pts[0].y;
1544 widthDst = pts[1].x - pts[0].x;
1545 heightDst = pts[1].y - pts[0].y;
1547 pts[0].x = xSrc;
1548 pts[0].y = ySrc;
1549 pts[1].x = xSrc + widthSrc;
1550 pts[1].y = ySrc + heightSrc;
1551 LPtoDP(devSrc->hdc, pts, 2);
1552 xSrc = pts[0].x;
1553 ySrc = pts[0].y;
1554 widthSrc = pts[1].x - pts[0].x;
1555 heightSrc = pts[1].y - pts[0].y;
1556 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1558 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1559 if(widthDst != widthSrc || heightDst != heightSrc)
1560 #else
1561 if(!pXRenderSetPictureTransform)
1562 #endif
1564 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1565 return FALSE;
1568 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1570 static BOOL out = FALSE;
1571 if (!out)
1573 FIXME("not a dibsection\n");
1574 out = TRUE;
1576 return FALSE;
1579 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1580 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1582 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1583 SetLastError(ERROR_INVALID_PARAMETER);
1584 return FALSE;
1587 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1588 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1590 if(dib.dsBm.bmBitsPixel != 32) {
1591 FIXME("not a 32 bpp dibsection\n");
1592 return FALSE;
1594 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1596 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1597 top_down = TRUE;
1598 dstbits += widthSrc * (heightSrc - 1);
1599 y2 = ySrc;
1600 y = y2 + heightSrc - 1;
1602 else
1604 y = dib.dsBmih.biHeight - ySrc - 1;
1605 y2 = y - heightSrc + 1;
1608 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1610 for(; y >= y2; y--)
1612 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1613 widthSrc * 4);
1614 dstbits += (top_down ? -1 : 1) * widthSrc;
1617 else
1619 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1620 int x;
1622 for(; y >= y2; y--)
1624 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1625 for (x = 0; x < widthSrc; x++)
1627 DWORD argb = *srcbits++;
1628 argb = (argb & 0xffffff) | source_alpha;
1629 *dstbits++ = argb;
1631 if (top_down) /* we traversed the row forward so we should go back by two rows */
1632 dstbits -= 2 * widthSrc;
1637 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1639 wine_tsx11_lock();
1640 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1641 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1644 Avoid using XRenderFindStandardFormat as older libraries don't have it
1645 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1647 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1649 TRACE("src_format %p\n", src_format);
1651 pa.subwindow_mode = IncludeInferiors;
1653 /* FIXME use devDst->xrender->pict ? */
1654 dst_pict = pXRenderCreatePicture(gdi_display,
1655 devDst->drawable,
1656 pict_formats[dst_depth_type],
1657 CPSubwindowMode, &pa);
1658 TRACE("dst_pict %08lx\n", dst_pict);
1659 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1660 xpm = XCreatePixmap(gdi_display,
1661 root_window,
1662 widthSrc, heightSrc, 32);
1663 gcv.graphics_exposures = False;
1664 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1665 TRACE("xpm = %08lx\n", xpm);
1666 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1668 src_pict = pXRenderCreatePicture(gdi_display,
1669 xpm, src_format,
1670 CPSubwindowMode, &pa);
1671 TRACE("src_pict %08lx\n", src_pict);
1673 if (rgndata)
1675 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1676 devDst->dc_rect.left, devDst->dc_rect.top,
1677 (XRectangle *)rgndata->Buffer,
1678 rgndata->rdh.nCount );
1679 HeapFree( GetProcessHeap(), 0, rgndata );
1682 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1683 if(widthDst != widthSrc || heightDst != heightSrc) {
1684 double xscale = widthSrc/(double)widthDst;
1685 double yscale = heightSrc/(double)heightDst;
1686 XTransform xform = {{
1687 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1688 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1689 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1691 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1693 #endif
1694 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1695 0, 0, 0, 0,
1696 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1699 pXRenderFreePicture(gdi_display, src_pict);
1700 XFreePixmap(gdi_display, xpm);
1701 XFreeGC(gdi_display, gc);
1702 pXRenderFreePicture(gdi_display, dst_pict);
1703 image->data = NULL;
1704 XDestroyImage(image);
1706 wine_tsx11_unlock();
1707 HeapFree(GetProcessHeap(), 0, data);
1708 return TRUE;
1711 #else /* SONAME_LIBXRENDER */
1713 void X11DRV_XRender_Init(void)
1715 TRACE("XRender support not compiled in.\n");
1716 return;
1719 void X11DRV_XRender_Finalize(void)
1723 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1725 assert(0);
1726 return FALSE;
1729 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1731 assert(0);
1732 return;
1735 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1736 const RECT *lprect, LPCWSTR wstr, UINT count,
1737 const INT *lpDx )
1739 assert(0);
1740 return FALSE;
1743 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1745 assert(0);
1746 return;
1749 /******************************************************************************
1750 * AlphaBlend (x11drv.@)
1752 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1753 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1754 BLENDFUNCTION blendfn)
1756 FIXME("not supported - XRENDER headers were missing at compile time\n");
1757 return FALSE;
1760 #endif /* SONAME_LIBXRENDER */