Changes in crossover-wine-src-6.1.0 except for configure
[wine/hacks.git] / dlls / winex11.drv / xrender.c
blob1a4f7dfa1a7b73f9acc208f767f5f4f874c2c838
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 "wownt32.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 static BOOL X11DRV_XRender_Installed = FALSE;
41 int using_client_side_fonts = FALSE;
43 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
45 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
47 #include <X11/Xlib.h>
48 #include <X11/extensions/Xrender.h>
50 static XRenderPictFormat *screen_format; /* format of screen */
51 static XRenderPictFormat *mono_format; /* format of mono bitmap */
53 typedef struct
55 LOGFONTW lf;
56 SIZE devsize; /* size in device coords */
57 DWORD hash;
58 } LFANDSIZE;
60 #define INITIAL_REALIZED_BUF_SIZE 128
62 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
64 typedef struct
66 GlyphSet glyphset;
67 XRenderPictFormat *font_format;
68 int nrealized;
69 BOOL *realized;
70 void **bitmaps;
71 XGlyphInfo *gis;
72 } gsCacheEntryFormat;
74 typedef struct
76 LFANDSIZE lfsz;
77 AA_Type aa_default;
78 gsCacheEntryFormat * format[AA_MAXVALUE];
79 INT count;
80 INT next;
81 } gsCacheEntry;
83 struct tagXRENDERINFO
85 int cache_index;
86 Picture pict;
87 Picture tile_pict;
88 Pixmap tile_xpm;
89 COLORREF lastTextColor;
93 static gsCacheEntry *glyphsetCache = NULL;
94 static DWORD glyphsetCacheSize = 0;
95 static INT lastfree = -1;
96 static INT mru = -1;
98 #define INIT_CACHE_SIZE 10
100 static int antialias = 1;
102 /* some default values just in case */
103 #ifndef SONAME_LIBX11
104 #define SONAME_LIBX11 "libX11.so"
105 #endif
106 #ifndef SONAME_LIBXEXT
107 #define SONAME_LIBXEXT "libXext.so"
108 #endif
109 #ifndef SONAME_LIBXRENDER
110 #define SONAME_LIBXRENDER "libXrender.so"
111 #endif
113 static void *xrender_handle;
115 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
116 MAKE_FUNCPTR(XRenderAddGlyphs)
117 MAKE_FUNCPTR(XRenderComposite)
118 MAKE_FUNCPTR(XRenderCompositeString8)
119 MAKE_FUNCPTR(XRenderCompositeString16)
120 MAKE_FUNCPTR(XRenderCompositeString32)
121 MAKE_FUNCPTR(XRenderCreateGlyphSet)
122 MAKE_FUNCPTR(XRenderCreatePicture)
123 MAKE_FUNCPTR(XRenderFillRectangle)
124 MAKE_FUNCPTR(XRenderFindFormat)
125 MAKE_FUNCPTR(XRenderFindVisualFormat)
126 MAKE_FUNCPTR(XRenderFreeGlyphSet)
127 MAKE_FUNCPTR(XRenderFreePicture)
128 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
129 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
130 MAKE_FUNCPTR(XRenderSetPictureTransform)
131 #endif
132 MAKE_FUNCPTR(XRenderQueryExtension)
133 #undef MAKE_FUNCPTR
135 static CRITICAL_SECTION xrender_cs;
136 static CRITICAL_SECTION_DEBUG critsect_debug =
138 0, 0, &xrender_cs,
139 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
140 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
142 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
144 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
145 ( ( (ULONG)_x4 << 24 ) | \
146 ( (ULONG)_x3 << 16 ) | \
147 ( (ULONG)_x2 << 8 ) | \
148 (ULONG)_x1 )
150 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
151 #define MS_OS2_TAG MS_MAKE_TAG('O', 'S', '/', '2')
153 #define GASP_GRIDFIT 0x01
154 #define GASP_DOGRAY 0x02
156 #ifdef WORDS_BIGENDIAN
157 #define get_be_word(x) (x)
158 #else
159 #define get_be_word(x) RtlUshortByteSwap(x)
160 #endif
162 /***********************************************************************
163 * stack_kludge
165 * XRender will succeed if an automatic variable has a non zero value.
166 * This kludge makes that more likely; we do minor tricks to attempt
167 * to make sure we don't get optimized away.
170 static void stack_kludge(void)
172 volatile char clearme[20000];
173 memset((char *) clearme, 1, sizeof(clearme));
176 /***********************************************************************
177 * X11DRV_XRender_Init
179 * Let's see if our XServer has the extension available
182 void X11DRV_XRender_Init(void)
184 int event_base, i;
185 XRenderPictFormat pf;
187 if (client_side_with_render &&
188 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
189 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
190 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
193 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
194 LOAD_FUNCPTR(XRenderAddGlyphs)
195 LOAD_FUNCPTR(XRenderComposite)
196 LOAD_FUNCPTR(XRenderCompositeString8)
197 LOAD_FUNCPTR(XRenderCompositeString16)
198 LOAD_FUNCPTR(XRenderCompositeString32)
199 LOAD_FUNCPTR(XRenderCreateGlyphSet)
200 LOAD_FUNCPTR(XRenderCreatePicture)
201 LOAD_FUNCPTR(XRenderFillRectangle)
202 LOAD_FUNCPTR(XRenderFindFormat)
203 LOAD_FUNCPTR(XRenderFindVisualFormat)
204 LOAD_FUNCPTR(XRenderFreeGlyphSet)
205 LOAD_FUNCPTR(XRenderFreePicture)
206 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
207 LOAD_FUNCPTR(XRenderQueryExtension)
208 #undef LOAD_FUNCPTR
209 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
210 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
211 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
212 #undef LOAD_OPTIONAL_FUNCPTR
213 #endif
216 wine_tsx11_lock();
217 if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
218 X11DRV_XRender_Installed = TRUE;
219 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
220 screen_format = pXRenderFindVisualFormat(gdi_display, visual);
222 /*--------------------------------------------------------------------
223 ** Xrender.c bug. (Version 1.4 of Xrender.c had a bug where
224 ** an uninitialized stack variable could cause this to fail.
225 ** This is fixed in version 1.5, which seems to correspond to
226 ** Xfree >= 4.2
227 ** In the mean time, we do a total kludge to attempt to address this:
228 ** we call it twice, and pray that the second time it is
229 ** initialized (and nudge the stack while we're at it).
230 ** If it doesn't, we just bail.
231 ** JPW 02/12/02
232 **------------------------------------------------------------------*/
233 if(!screen_format) {
234 WARN("XRenderFindVisualFormat failed; likely XFree bug. Retrying with stack kludge.\n");
235 stack_kludge();
236 screen_format = pXRenderFindVisualFormat(gdi_display, visual);
238 if(!screen_format)
240 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
241 if (visual->class == DirectColor)
243 XVisualInfo info;
244 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
245 screen_depth, TrueColor, &info ))
247 screen_format = pXRenderFindVisualFormat(gdi_display, info.visual);
248 if (screen_format) visual = info.visual;
252 if(!screen_format) /* This fails in buggy versions of libXrender.so */
254 wine_tsx11_unlock();
255 ERR("Your version of X has a buggy XRender extension (need >= 4.2). Disabling XRENDER.\n");
256 X11DRV_XRender_Installed = FALSE;
257 return;
259 pf.type = PictTypeDirect;
260 pf.depth = 1;
261 pf.direct.alpha = 0;
262 pf.direct.alphaMask = 1;
263 mono_format = pXRenderFindFormat(gdi_display, PictFormatType |
264 PictFormatDepth | PictFormatAlpha |
265 PictFormatAlphaMask, &pf, 0);
266 if(!mono_format) {
267 ERR("mono_format == NULL?\n");
268 X11DRV_XRender_Installed = FALSE;
270 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
271 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
272 X11DRV_XRender_Installed = FALSE;
275 wine_tsx11_unlock();
278 sym_not_found:
279 if(X11DRV_XRender_Installed || client_side_with_core)
281 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
282 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
284 glyphsetCacheSize = INIT_CACHE_SIZE;
285 lastfree = 0;
286 for(i = 0; i < INIT_CACHE_SIZE; i++) {
287 glyphsetCache[i].next = i + 1;
288 glyphsetCache[i].count = -1;
290 glyphsetCache[i-1].next = -1;
291 using_client_side_fonts = 1;
293 if(!X11DRV_XRender_Installed) {
294 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
295 if(screen_depth <= 8 || !client_side_antialias_with_core)
296 antialias = 0;
297 } else {
298 if(screen_depth <= 8 || !client_side_antialias_with_render)
299 antialias = 0;
302 else TRACE("Using X11 core fonts\n");
305 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
307 if(p1->hash != p2->hash) return TRUE;
308 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
309 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
310 return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
313 #if 0
314 static void walk_cache(void)
316 int i;
318 EnterCriticalSection(&xrender_cs);
319 for(i=mru; i >= 0; i = glyphsetCache[i].next)
320 TRACE("item %d\n", i);
321 LeaveCriticalSection(&xrender_cs);
323 #endif
325 static int LookupEntry(LFANDSIZE *plfsz)
327 int i, prev_i = -1;
329 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
330 TRACE("%d\n", i);
331 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
332 i = -1;
333 break;
336 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
337 glyphsetCache[i].count++;
338 if(prev_i >= 0) {
339 glyphsetCache[prev_i].next = glyphsetCache[i].next;
340 glyphsetCache[i].next = mru;
341 mru = i;
343 TRACE("found font in cache %d\n", i);
344 return i;
346 prev_i = i;
348 TRACE("font not in cache\n");
349 return -1;
352 static void FreeEntry(int entry)
354 int i, format;
356 for(format = 0; format < AA_MAXVALUE; format++) {
357 gsCacheEntryFormat * formatEntry;
359 if( !glyphsetCache[entry].format[format] )
360 continue;
362 formatEntry = glyphsetCache[entry].format[format];
364 if(formatEntry->glyphset) {
365 wine_tsx11_lock();
366 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
367 wine_tsx11_unlock();
368 formatEntry->glyphset = 0;
370 if(formatEntry->nrealized) {
371 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
372 formatEntry->realized = NULL;
373 if(formatEntry->bitmaps) {
374 for(i = 0; i < formatEntry->nrealized; i++)
375 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
376 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
377 formatEntry->bitmaps = NULL;
378 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
379 formatEntry->gis = NULL;
381 formatEntry->nrealized = 0;
384 HeapFree(GetProcessHeap(), 0, formatEntry);
385 glyphsetCache[entry].format[format] = NULL;
389 static int AllocEntry(void)
391 int best = -1, prev_best = -1, i, prev_i = -1;
393 if(lastfree >= 0) {
394 assert(glyphsetCache[lastfree].count == -1);
395 glyphsetCache[lastfree].count = 1;
396 best = lastfree;
397 lastfree = glyphsetCache[lastfree].next;
398 assert(best != mru);
399 glyphsetCache[best].next = mru;
400 mru = best;
402 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
403 return mru;
406 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
407 if(glyphsetCache[i].count == 0) {
408 best = i;
409 prev_best = prev_i;
411 prev_i = i;
414 if(best >= 0) {
415 TRACE("freeing unused glyphset at cache %d\n", best);
416 FreeEntry(best);
417 glyphsetCache[best].count = 1;
418 if(prev_best >= 0) {
419 glyphsetCache[prev_best].next = glyphsetCache[best].next;
420 glyphsetCache[best].next = mru;
421 mru = best;
422 } else {
423 assert(mru == best);
425 return mru;
428 TRACE("Growing cache\n");
430 if (glyphsetCache)
431 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
432 glyphsetCache,
433 (glyphsetCacheSize + INIT_CACHE_SIZE)
434 * sizeof(*glyphsetCache));
435 else
436 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
437 (glyphsetCacheSize + INIT_CACHE_SIZE)
438 * sizeof(*glyphsetCache));
440 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
441 i++) {
442 glyphsetCache[i].next = i + 1;
443 glyphsetCache[i].count = -1;
445 glyphsetCache[i-1].next = -1;
446 glyphsetCacheSize += INIT_CACHE_SIZE;
448 lastfree = glyphsetCache[best].next;
449 glyphsetCache[best].count = 1;
450 glyphsetCache[best].next = mru;
451 mru = best;
452 TRACE("new free cache slot at %d\n", mru);
453 return mru;
456 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
458 DWORD size;
459 WORD *gasp, *buffer;
460 WORD num_recs;
461 DWORD ppem;
462 TEXTMETRICW tm;
464 *flags = 0;
466 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
467 if(size == GDI_ERROR)
468 return FALSE;
470 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
471 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
473 GetTextMetricsW(physDev->hdc, &tm);
474 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
476 gasp++;
477 num_recs = get_be_word(*gasp);
478 gasp++;
479 while(num_recs--)
481 *flags = get_be_word(*(gasp + 1));
482 if(ppem <= get_be_word(*gasp))
483 break;
484 gasp += 2;
486 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
488 HeapFree(GetProcessHeap(), 0, buffer);
489 return TRUE;
492 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
494 int ret;
495 int format;
496 gsCacheEntry *entry;
497 WORD flags;
498 static int hinter = -1;
500 if((ret = LookupEntry(plfsz)) != -1) return ret;
502 ret = AllocEntry();
503 entry = glyphsetCache + ret;
504 entry->lfsz = *plfsz;
505 for( format = 0; format < AA_MAXVALUE; format++ ) {
506 assert( !entry->format[format] );
509 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
511 if(hinter == -1)
513 RASTERIZER_STATUS status;
514 GetRasterizerCaps(&status, sizeof(status));
515 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
517 if(!hinter || ignore_gasp || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
518 entry->aa_default = AA_Grey;
519 else
520 entry->aa_default = AA_None;
522 if(dont_aa_chinese)
524 DWORD size = GetFontData(physDev->hdc, MS_OS2_TAG, 0, NULL, 0);
525 if(size != GDI_ERROR)
527 WORD *os2 = HeapAlloc(GetProcessHeap(), 0, size);
528 DWORD csb0;
530 GetFontData(physDev->hdc, MS_OS2_TAG, 0, os2, size);
531 if(get_be_word(*os2) >= 1)
533 csb0 = get_be_word(*(os2 + 0x27)) << 16;
534 csb0 |= get_be_word(*(os2 + 0x28));
535 TRACE("csb0 = %08x\n", csb0);
536 if(csb0 & 0x00140000)
537 entry->aa_default = AA_None;
539 HeapFree(GetProcessHeap(), 0, os2);
543 else
544 entry->aa_default = AA_None;
546 return ret;
549 static void dec_ref_cache(int index)
551 assert(index >= 0);
552 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
553 assert(glyphsetCache[index].count > 0);
554 glyphsetCache[index].count--;
557 static void lfsz_calc_hash(LFANDSIZE *plfsz)
559 DWORD hash = 0, *ptr;
560 int i;
562 hash ^= plfsz->devsize.cx;
563 hash ^= plfsz->devsize.cy;
564 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
565 hash ^= *ptr;
566 for(i = 0, ptr = (DWORD*)&plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
567 WCHAR *pwc = (WCHAR *)ptr;
568 if(!*pwc) break;
569 hash ^= *ptr;
570 pwc++;
571 if(!*pwc) break;
573 plfsz->hash = hash;
574 return;
577 /***********************************************************************
578 * X11DRV_XRender_Finalize
580 void X11DRV_XRender_Finalize(void)
582 int i;
584 EnterCriticalSection(&xrender_cs);
585 for(i = mru; i >= 0; i = glyphsetCache[i].next)
586 FreeEntry(i);
587 LeaveCriticalSection(&xrender_cs);
591 /***********************************************************************
592 * X11DRV_XRender_SelectFont
594 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
596 LFANDSIZE lfsz;
598 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
599 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
600 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
601 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
602 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
603 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
604 lfsz_calc_hash(&lfsz);
606 EnterCriticalSection(&xrender_cs);
607 if(!physDev->xrender) {
608 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
609 sizeof(*physDev->xrender));
610 physDev->xrender->cache_index = -1;
612 else if(physDev->xrender->cache_index != -1)
613 dec_ref_cache(physDev->xrender->cache_index);
614 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
615 LeaveCriticalSection(&xrender_cs);
616 return 0;
619 /***********************************************************************
620 * X11DRV_XRender_DeleteDC
622 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
624 X11DRV_XRender_UpdateDrawable(physDev);
626 EnterCriticalSection(&xrender_cs);
627 if(physDev->xrender->cache_index != -1)
628 dec_ref_cache(physDev->xrender->cache_index);
629 LeaveCriticalSection(&xrender_cs);
631 HeapFree(GetProcessHeap(), 0, physDev->xrender);
632 physDev->xrender = NULL;
633 return;
636 /***********************************************************************
637 * X11DRV_XRender_UpdateDrawable
639 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
640 * It deletes the pict and tile when the drawable changes.
642 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
644 wine_tsx11_lock();
646 if(physDev->xrender->pict)
648 TRACE("freeing pict %08lx from dc %p drawable %08lx\n", physDev->xrender->pict,
649 physDev->hdc, physDev->drawable);
650 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
651 physDev->xrender->pict = 0;
653 if(physDev->xrender->tile_pict)
655 pXRenderFreePicture(gdi_display, physDev->xrender->tile_pict);
656 physDev->xrender->tile_pict = 0;
658 if(physDev->xrender->tile_xpm)
660 XFreePixmap(gdi_display, physDev->xrender->tile_xpm);
661 physDev->xrender->tile_xpm = 0;
664 wine_tsx11_unlock();
666 return;
669 /************************************************************************
670 * UploadGlyph
672 * Helper to ExtTextOut. Must be called inside xrender_cs
674 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
676 unsigned int buflen;
677 char *buf;
678 Glyph gid;
679 GLYPHMETRICS gm;
680 XGlyphInfo gi;
681 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
682 gsCacheEntryFormat *formatEntry;
683 UINT ggo_format = GGO_GLYPH_INDEX;
684 XRenderPictFormat pf;
686 switch(format) {
687 case AA_Grey:
688 ggo_format |= WINE_GGO_GRAY16_BITMAP;
689 break;
691 default:
692 ERR("aa = %d - not implemented\n", format);
693 case AA_None:
694 ggo_format |= GGO_BITMAP;
695 break;
698 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
699 NULL);
700 if(buflen == GDI_ERROR) {
701 if(format != AA_None) {
702 format = AA_None;
703 entry->aa_default = AA_None;
704 ggo_format &= ~WINE_GGO_GRAY16_BITMAP;
705 ggo_format |= GGO_BITMAP;
706 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
707 NULL);
709 if(buflen == GDI_ERROR) {
710 WARN("GetGlyphOutlineW failed\n");
711 return FALSE;
713 TRACE("Turning off antialiasing for this monochrome font\n");
716 /* If there is nothing for the current type, we create the entry. */
717 if( !entry->format[format] ) {
718 entry->format[format] = HeapAlloc(GetProcessHeap(),
719 HEAP_ZERO_MEMORY,
720 sizeof(gsCacheEntryFormat));
722 formatEntry = entry->format[format];
724 if(formatEntry->nrealized <= glyph) {
725 formatEntry->nrealized = (glyph / 128 + 1) * 128;
727 if (formatEntry->realized)
728 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
729 HEAP_ZERO_MEMORY,
730 formatEntry->realized,
731 formatEntry->nrealized * sizeof(BOOL));
732 else
733 formatEntry->realized = HeapAlloc(GetProcessHeap(),
734 HEAP_ZERO_MEMORY,
735 formatEntry->nrealized * sizeof(BOOL));
737 if(!X11DRV_XRender_Installed) {
738 if (formatEntry->bitmaps)
739 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
740 HEAP_ZERO_MEMORY,
741 formatEntry->bitmaps,
742 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
743 else
744 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
745 HEAP_ZERO_MEMORY,
746 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
748 if (formatEntry->gis)
749 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
750 HEAP_ZERO_MEMORY,
751 formatEntry->gis,
752 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
753 else
754 formatEntry->gis = HeapAlloc(GetProcessHeap(),
755 HEAP_ZERO_MEMORY,
756 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
761 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
762 switch(format) {
763 case AA_Grey:
764 pf.depth = 8;
765 pf.direct.alphaMask = 0xff;
766 break;
768 default:
769 ERR("aa = %d - not implemented\n", format);
770 case AA_None:
771 pf.depth = 1;
772 pf.direct.alphaMask = 1;
773 break;
776 pf.type = PictTypeDirect;
777 pf.direct.alpha = 0;
779 wine_tsx11_lock();
780 formatEntry->font_format = pXRenderFindFormat(gdi_display,
781 PictFormatType |
782 PictFormatDepth |
783 PictFormatAlpha |
784 PictFormatAlphaMask,
785 &pf, 0);
787 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
788 wine_tsx11_unlock();
792 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
793 if(!buf)
795 ERR("HeapAlloc of %d bytes failed\n",buflen);
796 return FALSE;
798 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, NULL);
799 formatEntry->realized[glyph] = TRUE;
801 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
802 buflen,
803 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
804 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
806 gi.width = gm.gmBlackBoxX;
807 gi.height = gm.gmBlackBoxY;
808 gi.x = -gm.gmptGlyphOrigin.x;
809 gi.y = gm.gmptGlyphOrigin.y;
810 gi.xOff = gm.gmCellIncX;
811 gi.yOff = gm.gmCellIncY;
813 if(TRACE_ON(xrender)) {
814 int pitch, i, j;
815 char output[300];
816 unsigned char *line;
818 if(format == AA_None) {
819 pitch = ((gi.width + 31) / 32) * 4;
820 for(i = 0; i < gi.height; i++) {
821 line = (unsigned char*) buf + i * pitch;
822 output[0] = '\0';
823 for(j = 0; j < pitch * 8; j++) {
824 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
826 strcat(output, "\n");
827 TRACE(output);
829 } else {
830 static const char blks[] = " .:;!o*#";
831 char str[2];
833 str[1] = '\0';
834 pitch = ((gi.width + 3) / 4) * 4;
835 for(i = 0; i < gi.height; i++) {
836 line = (unsigned char*) buf + i * pitch;
837 output[0] = '\0';
838 for(j = 0; j < pitch; j++) {
839 str[0] = blks[line[j] >> 5];
840 strcat(output, str);
842 strcat(output, "\n");
843 TRACE(output);
848 if(formatEntry->glyphset) {
849 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
850 unsigned char *byte = (unsigned char*) buf, c;
851 int i = buflen;
853 while(i--) {
854 c = *byte;
856 /* magic to flip bit order */
857 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
858 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
859 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
861 *byte++ = c;
864 gid = glyph;
865 wine_tsx11_lock();
866 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
867 buf, buflen);
868 wine_tsx11_unlock();
869 HeapFree(GetProcessHeap(), 0, buf);
870 } else {
871 formatEntry->bitmaps[glyph] = buf;
872 memcpy(&formatEntry->gis[glyph], &gi, sizeof(gi));
874 return TRUE;
877 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
878 void *bitmap, XGlyphInfo *gi)
880 unsigned char *srcLine = bitmap, *src;
881 unsigned char bits, bitsMask;
882 int width = gi->width;
883 int stride = ((width + 31) & ~31) >> 3;
884 int height = gi->height;
885 int w;
886 int xspan, lenspan;
888 TRACE("%d, %d\n", x, y);
889 x -= gi->x;
890 y -= gi->y;
891 while (height--)
893 src = srcLine;
894 srcLine += stride;
895 w = width;
897 bitsMask = 0x80; /* FreeType is always MSB first */
898 bits = *src++;
900 xspan = x;
901 while (w)
903 if (bits & bitsMask)
905 lenspan = 0;
908 lenspan++;
909 if (lenspan == w)
910 break;
911 bitsMask = bitsMask >> 1;
912 if (!bitsMask)
914 bits = *src++;
915 bitsMask = 0x80;
917 } while (bits & bitsMask);
918 XFillRectangle (gdi_display, physDev->drawable,
919 physDev->gc, xspan, y, lenspan, 1);
920 xspan += lenspan;
921 w -= lenspan;
923 else
927 w--;
928 xspan++;
929 if (!w)
930 break;
931 bitsMask = bitsMask >> 1;
932 if (!bitsMask)
934 bits = *src++;
935 bitsMask = 0x80;
937 } while (!(bits & bitsMask));
940 y++;
944 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
945 void *bitmap, XGlyphInfo *gi)
947 unsigned char *srcLine = bitmap, *src, bits;
948 int width = gi->width;
949 int stride = ((width + 3) & ~3);
950 int height = gi->height;
951 int w;
952 int xspan, lenspan;
954 x -= gi->x;
955 y -= gi->y;
956 while (height--)
958 src = srcLine;
959 srcLine += stride;
960 w = width;
962 bits = *src++;
963 xspan = x;
964 while (w)
966 if (bits >= 0x80)
968 lenspan = 0;
971 lenspan++;
972 if (lenspan == w)
973 break;
974 bits = *src++;
975 } while (bits >= 0x80);
976 XFillRectangle (gdi_display, physDev->drawable,
977 physDev->gc, xspan, y, lenspan, 1);
978 xspan += lenspan;
979 w -= lenspan;
981 else
985 w--;
986 xspan++;
987 if (!w)
988 break;
989 bits = *src++;
990 } while (bits < 0x80);
993 y++;
998 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1000 int s, l;
1002 s = 0;
1003 while ((mask & 1) == 0)
1005 mask >>= 1;
1006 s++;
1008 l = 0;
1009 while ((mask & 1) == 1)
1011 mask >>= 1;
1012 l++;
1014 *shift = s;
1015 *len = l;
1018 static DWORD GetField (DWORD pixel, int shift, int len)
1020 pixel = pixel & (((1 << (len)) - 1) << shift);
1021 pixel = pixel << (32 - (shift + len)) >> 24;
1022 while (len < 8)
1024 pixel |= (pixel >> len);
1025 len <<= 1;
1027 return pixel;
1031 static DWORD PutField (DWORD pixel, int shift, int len)
1033 shift = shift - (8 - len);
1034 if (len <= 8)
1035 pixel &= (((1 << len) - 1) << (8 - len));
1036 if (shift < 0)
1037 pixel >>= -shift;
1038 else
1039 pixel <<= shift;
1040 return pixel;
1043 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1044 int color)
1046 int r_shift, r_len;
1047 int g_shift, g_len;
1048 int b_shift, b_len;
1049 BYTE *maskLine, *mask, m;
1050 int maskStride;
1051 DWORD pixel;
1052 int width, height;
1053 int w, tx;
1054 BYTE src_r, src_g, src_b;
1056 x -= gi->x;
1057 y -= gi->y;
1058 width = gi->width;
1059 height = gi->height;
1061 maskLine = (unsigned char *) bitmap;
1062 maskStride = (width + 3) & ~3;
1064 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1065 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1066 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1068 src_r = GetField(color, r_shift, r_len);
1069 src_g = GetField(color, g_shift, g_len);
1070 src_b = GetField(color, b_shift, b_len);
1072 for(; height--; y++)
1074 mask = maskLine;
1075 maskLine += maskStride;
1076 w = width;
1077 tx = x;
1079 if(y < 0) continue;
1080 if(y >= image->height) break;
1082 for(; w--; tx++)
1084 if(tx >= image->width) break;
1086 m = *mask++;
1087 if(tx < 0) continue;
1089 if (m == 0xff)
1090 XPutPixel (image, tx, y, color);
1091 else if (m)
1093 BYTE r, g, b;
1095 pixel = XGetPixel (image, tx, y);
1097 r = GetField(pixel, r_shift, r_len);
1098 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1099 g = GetField(pixel, g_shift, g_len);
1100 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1101 b = GetField(pixel, b_shift, b_len);
1102 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1104 pixel = (PutField (r, r_shift, r_len) |
1105 PutField (g, g_shift, g_len) |
1106 PutField (b, b_shift, b_len));
1107 XPutPixel (image, tx, y, pixel);
1113 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1115 return 1;
1118 /***********************************************************************
1119 * X11DRV_XRender_ExtTextOut
1121 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1122 const RECT *lprect, LPCWSTR wstr, UINT count,
1123 const INT *lpDx )
1125 XRenderColor col;
1126 RGNDATA *data;
1127 XGCValues xgcval;
1128 int render_op = PictOpOver;
1129 gsCacheEntry *entry;
1130 gsCacheEntryFormat *formatEntry;
1131 BOOL retv = FALSE;
1132 HDC hdc = physDev->hdc;
1133 int textPixel, backgroundPixel;
1134 HRGN saved_region = 0;
1135 BOOL disable_antialias = FALSE;
1136 AA_Type aa_type = AA_None;
1137 DIBSECTION bmp;
1138 unsigned int idx;
1139 double cosEsc, sinEsc;
1140 LOGFONTW lf;
1142 /* Do we need to disable antialiasing because of palette mode? */
1143 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1144 TRACE("bitmap is not a DIB\n");
1146 else if (bmp.dsBmih.biBitCount <= 8) {
1147 TRACE("Disabling antialiasing\n");
1148 disable_antialias = TRUE;
1151 xgcval.function = GXcopy;
1152 xgcval.background = physDev->backgroundPixel;
1153 xgcval.fill_style = FillSolid;
1154 wine_tsx11_lock();
1155 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1156 wine_tsx11_unlock();
1158 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod, FALSE );
1160 if(physDev->depth == 1) {
1161 if((physDev->textPixel & 0xffffff) == 0) {
1162 textPixel = 0;
1163 backgroundPixel = 1;
1164 } else {
1165 textPixel = 1;
1166 backgroundPixel = 0;
1168 } else {
1169 textPixel = physDev->textPixel;
1170 backgroundPixel = physDev->backgroundPixel;
1173 if(flags & ETO_OPAQUE)
1175 wine_tsx11_lock();
1176 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1177 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1178 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1179 lprect->right - lprect->left, lprect->bottom - lprect->top );
1180 wine_tsx11_unlock();
1183 if(count == 0)
1185 retv = TRUE;
1186 goto done_unlock;
1190 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1191 if(lf.lfEscapement != 0) {
1192 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1193 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1194 } else {
1195 cosEsc = 1;
1196 sinEsc = 0;
1199 if (flags & ETO_CLIPPED)
1201 HRGN clip_region;
1203 clip_region = CreateRectRgnIndirect( lprect );
1204 /* make a copy of the current device region */
1205 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1206 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1207 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1208 DeleteObject( clip_region );
1211 if(X11DRV_XRender_Installed) {
1212 if(!physDev->xrender->pict) {
1213 XRenderPictureAttributes pa;
1214 pa.subwindow_mode = IncludeInferiors;
1216 wine_tsx11_lock();
1217 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1218 physDev->drawable,
1219 (physDev->depth == 1) ?
1220 mono_format : screen_format,
1221 CPSubwindowMode, &pa);
1222 wine_tsx11_unlock();
1224 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1225 physDev->xrender->pict, hdc, physDev->drawable);
1226 } else {
1227 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1228 physDev->xrender->pict, hdc, physDev->drawable);
1231 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1233 wine_tsx11_lock();
1234 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1235 physDev->dc_rect.left, physDev->dc_rect.top,
1236 (XRectangle *)data->Buffer, data->rdh.nCount );
1237 wine_tsx11_unlock();
1238 HeapFree( GetProcessHeap(), 0, data );
1242 if(X11DRV_XRender_Installed) {
1243 /* Create a 1x1 pixmap to tile over the font mask */
1244 if(!physDev->xrender->tile_xpm) {
1245 XRenderPictureAttributes pa;
1247 XRenderPictFormat *format = (physDev->depth == 1) ? mono_format : screen_format;
1248 wine_tsx11_lock();
1249 physDev->xrender->tile_xpm = XCreatePixmap(gdi_display,
1250 physDev->drawable,
1251 1, 1,
1252 format->depth);
1253 pa.repeat = True;
1254 physDev->xrender->tile_pict = pXRenderCreatePicture(gdi_display,
1255 physDev->xrender->tile_xpm,
1256 format,
1257 CPRepeat, &pa);
1258 wine_tsx11_unlock();
1259 TRACE("Created pixmap of depth %d\n", format->depth);
1260 /* init lastTextColor to something different from textPixel */
1261 physDev->xrender->lastTextColor = ~physDev->textPixel;
1265 if(physDev->textPixel != physDev->xrender->lastTextColor) {
1266 if(physDev->depth != 1) {
1267 /* Map 0 -- 0xff onto 0 -- 0xffff */
1268 int r_shift, r_len;
1269 int g_shift, g_len;
1270 int b_shift, b_len;
1272 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1273 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1274 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1276 col.red = GetField(physDev->textPixel, r_shift, r_len);
1277 col.red |= col.red << 8;
1278 col.green = GetField(physDev->textPixel, g_shift, g_len);
1279 col.green |= col.green << 8;
1280 col.blue = GetField(physDev->textPixel, b_shift, b_len);
1281 col.blue |= col.blue << 8;
1282 col.alpha = 0x0;
1283 } else { /* for a 1bpp bitmap we always need a 1 in the tile */
1284 col.red = col.green = col.blue = 0;
1285 col.alpha = 0xffff;
1287 wine_tsx11_lock();
1288 pXRenderFillRectangle(gdi_display, PictOpSrc,
1289 physDev->xrender->tile_pict,
1290 &col, 0, 0, 1, 1);
1291 wine_tsx11_unlock();
1292 physDev->xrender->lastTextColor = physDev->textPixel;
1295 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1297 if((physDev->depth == 1) && (textPixel == 0))
1298 render_op = PictOpOutReverse; /* This gives us 'black' text */
1301 EnterCriticalSection(&xrender_cs);
1302 entry = glyphsetCache + physDev->xrender->cache_index;
1303 if( disable_antialias == FALSE )
1304 aa_type = entry->aa_default;
1305 formatEntry = entry->format[aa_type];
1307 for(idx = 0; idx < count; idx++) {
1308 if( !formatEntry ) {
1309 UploadGlyph(physDev, wstr[idx], aa_type);
1310 /* re-evaluate antialias since aa_default may have changed */
1311 if( disable_antialias == FALSE )
1312 aa_type = entry->aa_default;
1313 formatEntry = entry->format[aa_type];
1314 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1315 UploadGlyph(physDev, wstr[idx], aa_type);
1318 if (!formatEntry)
1320 WARN("could not upload requested glyphs\n");
1321 LeaveCriticalSection(&xrender_cs);
1322 goto done_unlock;
1325 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1326 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1328 if(X11DRV_XRender_Installed) {
1329 wine_tsx11_lock();
1330 if(!lpDx)
1331 pXRenderCompositeString16(gdi_display, render_op,
1332 physDev->xrender->tile_pict,
1333 physDev->xrender->pict,
1334 formatEntry->font_format, formatEntry->glyphset,
1335 0, 0, physDev->dc_rect.left + x, physDev->dc_rect.top + y,
1336 wstr, count);
1337 else {
1338 INT offset = 0, xoff = 0, yoff = 0;
1339 for(idx = 0; idx < count; idx++) {
1340 pXRenderCompositeString16(gdi_display, render_op,
1341 physDev->xrender->tile_pict,
1342 physDev->xrender->pict,
1343 formatEntry->font_format, formatEntry->glyphset,
1344 0, 0, physDev->dc_rect.left + x + xoff,
1345 physDev->dc_rect.top + y + yoff,
1346 wstr + idx, 1);
1347 offset += lpDx[idx];
1348 xoff = offset * cosEsc;
1349 yoff = offset * -sinEsc;
1352 wine_tsx11_unlock();
1354 } else {
1355 INT offset = 0, xoff = 0, yoff = 0;
1356 wine_tsx11_lock();
1357 XSetForeground( gdi_display, physDev->gc, textPixel );
1359 if(aa_type == AA_None) {
1360 for(idx = 0; idx < count; idx++) {
1361 SharpGlyphMono(physDev, physDev->dc_rect.left + x + xoff,
1362 physDev->dc_rect.top + y + yoff,
1363 formatEntry->bitmaps[wstr[idx]],
1364 &formatEntry->gis[wstr[idx]]);
1365 if(lpDx) {
1366 offset += lpDx[idx];
1367 xoff = offset * cosEsc;
1368 yoff = offset * -sinEsc;
1369 } else {
1370 xoff += formatEntry->gis[wstr[idx]].xOff;
1371 yoff += formatEntry->gis[wstr[idx]].yOff;
1374 } else if(physDev->depth == 1) {
1375 for(idx = 0; idx < count; idx++) {
1376 SharpGlyphGray(physDev, physDev->dc_rect.left + x + xoff,
1377 physDev->dc_rect.top + y + yoff,
1378 formatEntry->bitmaps[wstr[idx]],
1379 &formatEntry->gis[wstr[idx]]);
1380 if(lpDx) {
1381 offset += lpDx[idx];
1382 xoff = offset * cosEsc;
1383 yoff = offset * -sinEsc;
1384 } else {
1385 xoff += formatEntry->gis[wstr[idx]].xOff;
1386 yoff += formatEntry->gis[wstr[idx]].yOff;
1390 } else {
1391 XImage *image;
1392 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1393 RECT extents = {0, 0, 0, 0};
1394 POINT cur = {0, 0};
1395 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1396 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1398 TRACE("drawable %dx%d\n", w, h);
1400 for(idx = 0; idx < count; idx++) {
1401 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1402 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1403 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1404 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1405 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1406 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1407 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1408 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1409 if(lpDx) {
1410 offset += lpDx[idx];
1411 cur.x = offset * cosEsc;
1412 cur.y = offset * -sinEsc;
1413 } else {
1414 cur.x += formatEntry->gis[wstr[idx]].xOff;
1415 cur.y += formatEntry->gis[wstr[idx]].yOff;
1418 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1419 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1421 if(physDev->dc_rect.left + x + extents.left >= 0) {
1422 image_x = physDev->dc_rect.left + x + extents.left;
1423 image_off_x = 0;
1424 } else {
1425 image_x = 0;
1426 image_off_x = physDev->dc_rect.left + x + extents.left;
1428 if(physDev->dc_rect.top + y + extents.top >= 0) {
1429 image_y = physDev->dc_rect.top + y + extents.top;
1430 image_off_y = 0;
1431 } else {
1432 image_y = 0;
1433 image_off_y = physDev->dc_rect.top + y + extents.top;
1435 if(physDev->dc_rect.left + x + extents.right < w)
1436 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1437 else
1438 image_w = w - image_x;
1439 if(physDev->dc_rect.top + y + extents.bottom < h)
1440 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1441 else
1442 image_h = h - image_y;
1444 if(image_w <= 0 || image_h <= 0) goto no_image;
1446 image = NULL;
1447 if(GetBkMode(hdc) != TRANSPARENT && lf.lfEscapement == 0) {
1448 /* We don't need a XGetImage in this case as we know the bkgnd colour
1449 already. We could also include ETO_OPAQUE here if we know that the
1450 rectangle is larger than the text extents */
1451 image = XCreateImage(gdi_display, visual, physDev->depth, ZPixmap, 0, NULL,
1452 image_w, image_h, 32, 0);
1453 image->data = calloc(image_h, image->bytes_per_line);
1454 XAddPixel(image, backgroundPixel);
1455 if(image) goto got_image;
1457 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1458 image = XGetImage(gdi_display, physDev->drawable,
1459 image_x, image_y, image_w, image_h,
1460 AllPlanes, ZPixmap);
1461 X11DRV_check_error();
1463 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1464 gdi_display, (int)physDev->drawable, image_x, image_y,
1465 image_w, image_h, AllPlanes, ZPixmap,
1466 physDev->depth, image);
1467 if(!image) {
1468 Pixmap xpm = XCreatePixmap(gdi_display, physDev->drawable, image_w, image_h,
1469 physDev->depth);
1470 GC gc;
1471 XGCValues gcv;
1473 gcv.graphics_exposures = False;
1474 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1475 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1476 image_w, image_h, 0, 0);
1477 XFreeGC(gdi_display, gc);
1478 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1479 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1480 ZPixmap);
1481 X11DRV_check_error();
1482 XFreePixmap(gdi_display, xpm);
1484 if(!image) goto no_image;
1486 got_image:
1487 image->red_mask = visual->red_mask;
1488 image->green_mask = visual->green_mask;
1489 image->blue_mask = visual->blue_mask;
1491 offset = xoff = yoff = 0;
1492 for(idx = 0; idx < count; idx++) {
1493 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1494 yoff + image_off_y - extents.top,
1495 formatEntry->bitmaps[wstr[idx]],
1496 &formatEntry->gis[wstr[idx]],
1497 physDev->textPixel);
1498 if(lpDx) {
1499 offset += lpDx[idx];
1500 xoff = offset * cosEsc;
1501 yoff = offset * -sinEsc;
1502 } else {
1503 xoff += formatEntry->gis[wstr[idx]].xOff;
1504 yoff += formatEntry->gis[wstr[idx]].yOff;
1507 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1508 image_x, image_y, image_w, image_h);
1509 XDestroyImage(image);
1511 no_image:
1512 wine_tsx11_unlock();
1514 LeaveCriticalSection(&xrender_cs);
1516 if (flags & ETO_CLIPPED)
1518 /* restore the device region */
1519 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1520 DeleteObject( saved_region );
1523 retv = TRUE;
1525 done_unlock:
1526 X11DRV_UnlockDIBSection( physDev, TRUE );
1527 return retv;
1530 /******************************************************************************
1531 * AlphaBlend (x11drv.@)
1533 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1534 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1535 BLENDFUNCTION blendfn)
1537 XRenderPictureAttributes pa;
1538 XRenderPictFormat *src_format;
1539 XRenderPictFormat argb32_templ = {
1540 0, /* id */
1541 PictTypeDirect, /* type */
1542 32, /* depth */
1543 { /* direct */
1544 16, /* direct.red */
1545 0xff, /* direct.redMask */
1546 8, /* direct.green */
1547 0xff, /* direct.greenMask */
1548 0, /* direct.blue */
1549 0xff, /* direct.blueMask */
1550 24, /* direct.alpha */
1551 0xff, /* direct.alphaMask */
1553 0, /* colormap */
1555 unsigned long argb32_templ_mask =
1556 PictFormatType |
1557 PictFormatDepth |
1558 PictFormatRed |
1559 PictFormatRedMask |
1560 PictFormatGreen |
1561 PictFormatGreenMask |
1562 PictFormatBlue |
1563 PictFormatBlueMask |
1564 PictFormatAlpha |
1565 PictFormatAlphaMask;
1567 Picture dst_pict, src_pict;
1568 Pixmap xpm;
1569 DIBSECTION dib;
1570 XImage *image;
1571 GC gc;
1572 XGCValues gcv;
1573 BYTE *dstbits, *data;
1574 int y, y2;
1575 POINT pts[2];
1576 BOOL top_down = FALSE;
1577 RGNDATA *rgndata;
1579 if(!X11DRV_XRender_Installed) {
1580 FIXME("Unable to AlphaBlend without Xrender\n");
1581 return FALSE;
1583 pts[0].x = xDst;
1584 pts[0].y = yDst;
1585 pts[1].x = xDst + widthDst;
1586 pts[1].y = yDst + heightDst;
1587 LPtoDP(devDst->hdc, pts, 2);
1588 xDst = pts[0].x;
1589 yDst = pts[0].y;
1590 widthDst = pts[1].x - pts[0].x;
1591 heightDst = pts[1].y - pts[0].y;
1593 pts[0].x = xSrc;
1594 pts[0].y = ySrc;
1595 pts[1].x = xSrc + widthSrc;
1596 pts[1].y = ySrc + heightSrc;
1597 LPtoDP(devSrc->hdc, pts, 2);
1598 xSrc = pts[0].x;
1599 ySrc = pts[0].y;
1600 widthSrc = pts[1].x - pts[0].x;
1601 heightSrc = pts[1].y - pts[0].y;
1602 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1604 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1605 if(widthDst != widthSrc || heightDst != heightSrc)
1606 #else
1607 if(!pXRenderSetPictureTransform)
1608 #endif
1610 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1611 return FALSE;
1614 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1616 FIXME("not a dibsection\n");
1617 return FALSE;
1620 if(dib.dsBm.bmBitsPixel != 32) {
1621 FIXME("not a 32 bpp dibsection\n");
1622 return FALSE;
1624 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1626 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1627 top_down = TRUE;
1628 dstbits += widthSrc * (heightSrc - 1) * 4;
1629 y2 = ySrc;
1630 y = y2 + heightSrc - 1;
1632 else
1634 y = dib.dsBmih.biHeight - ySrc - 1;
1635 y2 = y - heightSrc + 1;
1637 for(; y >= y2; y--) {
1638 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1639 widthSrc * 4);
1640 dstbits += (top_down ? -1 : 1) * widthSrc * 4;
1643 wine_tsx11_lock();
1644 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1645 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1648 Avoid using XRenderFindStandardFormat as older libraries don't have it
1649 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1651 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1653 TRACE("src_format %p\n", src_format);
1655 pa.subwindow_mode = IncludeInferiors;
1657 /* FIXME use devDst->xrender->pict ? */
1658 dst_pict = pXRenderCreatePicture(gdi_display,
1659 devDst->drawable,
1660 (devDst->depth == 1) ?
1661 mono_format : screen_format,
1662 CPSubwindowMode, &pa);
1663 TRACE("dst_pict %08lx\n", dst_pict);
1664 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1665 xpm = XCreatePixmap(gdi_display,
1666 devSrc->drawable,
1667 widthSrc, heightSrc, 32);
1668 gcv.graphics_exposures = False;
1669 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1670 TRACE("xpm = %08lx\n", xpm);
1671 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1673 src_pict = pXRenderCreatePicture(gdi_display,
1674 xpm, src_format,
1675 CPSubwindowMode, &pa);
1676 TRACE("src_pict %08lx\n", src_pict);
1678 if ((rgndata = X11DRV_GetRegionData( devDst->region, 0 )))
1680 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1681 devDst->dc_rect.left, devDst->dc_rect.top,
1682 (XRectangle *)rgndata->Buffer,
1683 rgndata->rdh.nCount );
1684 HeapFree( GetProcessHeap(), 0, rgndata );
1687 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1688 if(widthDst != widthSrc || heightDst != heightSrc) {
1689 double xscale = widthSrc/(double)widthDst;
1690 double yscale = heightSrc/(double)heightDst;
1691 XTransform xform = {{
1692 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1693 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1694 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1696 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1698 #endif
1699 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1700 0, 0, 0, 0,
1701 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1704 pXRenderFreePicture(gdi_display, src_pict);
1705 XFreePixmap(gdi_display, xpm);
1706 XFreeGC(gdi_display, gc);
1707 pXRenderFreePicture(gdi_display, dst_pict);
1708 image->data = NULL;
1709 XDestroyImage(image);
1711 wine_tsx11_unlock();
1712 HeapFree(GetProcessHeap(), 0, data);
1713 return TRUE;
1716 #else /* HAVE_X11_EXTENSIONS_XRENDER_H */
1718 void X11DRV_XRender_Init(void)
1720 TRACE("XRender support not compiled in.\n");
1721 return;
1724 void X11DRV_XRender_Finalize(void)
1728 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1730 assert(0);
1731 return FALSE;
1734 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1736 assert(0);
1737 return;
1740 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1741 const RECT *lprect, LPCWSTR wstr, UINT count,
1742 const INT *lpDx )
1744 assert(0);
1745 return FALSE;
1748 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1750 assert(0);
1751 return;
1754 /******************************************************************************
1755 * AlphaBlend (x11drv.@)
1757 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1758 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1759 BLENDFUNCTION blendfn)
1761 FIXME("not supported - XRENDER headers were missing at compile time\n");
1762 return FALSE;
1765 #endif /* HAVE_X11_EXTENSIONS_XRENDER_H */