push 94e35525417ba0671ead00a6c6dfaaed30f46234
[wine/hacks.git] / dlls / winex11.drv / xrender.c
blob01c95604a15483e15406998e982114f4680a0cb7
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 #define NATIVE_BYTE_ORDER MSBFirst
146 #else
147 #define get_be_word(x) RtlUshortByteSwap(x)
148 #define NATIVE_BYTE_ORDER LSBFirst
149 #endif
151 /***********************************************************************
152 * X11DRV_XRender_Init
154 * Let's see if our XServer has the extension available
157 void X11DRV_XRender_Init(void)
159 int event_base, i;
160 XRenderPictFormat pf;
162 if (client_side_with_render &&
163 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
164 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
165 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
168 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
169 LOAD_FUNCPTR(XRenderAddGlyphs)
170 LOAD_FUNCPTR(XRenderComposite)
171 LOAD_FUNCPTR(XRenderCompositeString8)
172 LOAD_FUNCPTR(XRenderCompositeString16)
173 LOAD_FUNCPTR(XRenderCompositeString32)
174 LOAD_FUNCPTR(XRenderCompositeText16)
175 LOAD_FUNCPTR(XRenderCreateGlyphSet)
176 LOAD_FUNCPTR(XRenderCreatePicture)
177 LOAD_FUNCPTR(XRenderFillRectangle)
178 LOAD_FUNCPTR(XRenderFindFormat)
179 LOAD_FUNCPTR(XRenderFindVisualFormat)
180 LOAD_FUNCPTR(XRenderFreeGlyphSet)
181 LOAD_FUNCPTR(XRenderFreePicture)
182 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
183 LOAD_FUNCPTR(XRenderQueryExtension)
184 #undef LOAD_FUNCPTR
185 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
186 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
187 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
188 #undef LOAD_OPTIONAL_FUNCPTR
189 #endif
192 wine_tsx11_lock();
193 if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
194 X11DRV_XRender_Installed = TRUE;
195 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
196 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
197 if(!pict_formats[color_drawable])
199 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
200 if (visual->class == DirectColor)
202 XVisualInfo info;
203 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
204 screen_depth, TrueColor, &info ))
206 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
207 if (pict_formats[color_drawable]) visual = info.visual;
211 if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
213 wine_tsx11_unlock();
214 WINE_MESSAGE(
215 "Wine has detected that you probably have a buggy version\n"
216 "of libXrender.so . Because of this client side font rendering\n"
217 "will be disabled. Please upgrade this library.\n");
218 X11DRV_XRender_Installed = FALSE;
219 return;
221 pf.type = PictTypeDirect;
222 pf.depth = 1;
223 pf.direct.alpha = 0;
224 pf.direct.alphaMask = 1;
225 pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
226 PictFormatDepth | PictFormatAlpha |
227 PictFormatAlphaMask, &pf, 0);
228 if(!pict_formats[mono_drawable]) {
229 ERR("mono_format == NULL?\n");
230 X11DRV_XRender_Installed = FALSE;
232 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
233 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
234 X11DRV_XRender_Installed = FALSE;
237 wine_tsx11_unlock();
240 sym_not_found:
241 if(X11DRV_XRender_Installed || client_side_with_core)
243 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
244 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
246 glyphsetCacheSize = INIT_CACHE_SIZE;
247 lastfree = 0;
248 for(i = 0; i < INIT_CACHE_SIZE; i++) {
249 glyphsetCache[i].next = i + 1;
250 glyphsetCache[i].count = -1;
252 glyphsetCache[i-1].next = -1;
253 using_client_side_fonts = 1;
255 if(!X11DRV_XRender_Installed) {
256 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
257 if(screen_depth <= 8 || !client_side_antialias_with_core)
258 antialias = 0;
259 } else {
260 if(screen_depth <= 8 || !client_side_antialias_with_render)
261 antialias = 0;
264 else TRACE("Using X11 core fonts\n");
267 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
269 if(p1->hash != p2->hash) return TRUE;
270 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
271 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
272 return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
275 #if 0
276 static void walk_cache(void)
278 int i;
280 EnterCriticalSection(&xrender_cs);
281 for(i=mru; i >= 0; i = glyphsetCache[i].next)
282 TRACE("item %d\n", i);
283 LeaveCriticalSection(&xrender_cs);
285 #endif
287 static int LookupEntry(LFANDSIZE *plfsz)
289 int i, prev_i = -1;
291 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
292 TRACE("%d\n", i);
293 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
294 i = -1;
295 break;
298 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
299 glyphsetCache[i].count++;
300 if(prev_i >= 0) {
301 glyphsetCache[prev_i].next = glyphsetCache[i].next;
302 glyphsetCache[i].next = mru;
303 mru = i;
305 TRACE("found font in cache %d\n", i);
306 return i;
308 prev_i = i;
310 TRACE("font not in cache\n");
311 return -1;
314 static void FreeEntry(int entry)
316 int i, format;
318 for(format = 0; format < AA_MAXVALUE; format++) {
319 gsCacheEntryFormat * formatEntry;
321 if( !glyphsetCache[entry].format[format] )
322 continue;
324 formatEntry = glyphsetCache[entry].format[format];
326 if(formatEntry->glyphset) {
327 wine_tsx11_lock();
328 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
329 wine_tsx11_unlock();
330 formatEntry->glyphset = 0;
332 if(formatEntry->nrealized) {
333 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
334 formatEntry->realized = NULL;
335 if(formatEntry->bitmaps) {
336 for(i = 0; i < formatEntry->nrealized; i++)
337 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
338 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
339 formatEntry->bitmaps = NULL;
341 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
342 formatEntry->gis = NULL;
343 formatEntry->nrealized = 0;
346 HeapFree(GetProcessHeap(), 0, formatEntry);
347 glyphsetCache[entry].format[format] = NULL;
351 static int AllocEntry(void)
353 int best = -1, prev_best = -1, i, prev_i = -1;
355 if(lastfree >= 0) {
356 assert(glyphsetCache[lastfree].count == -1);
357 glyphsetCache[lastfree].count = 1;
358 best = lastfree;
359 lastfree = glyphsetCache[lastfree].next;
360 assert(best != mru);
361 glyphsetCache[best].next = mru;
362 mru = best;
364 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
365 return mru;
368 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
369 if(glyphsetCache[i].count == 0) {
370 best = i;
371 prev_best = prev_i;
373 prev_i = i;
376 if(best >= 0) {
377 TRACE("freeing unused glyphset at cache %d\n", best);
378 FreeEntry(best);
379 glyphsetCache[best].count = 1;
380 if(prev_best >= 0) {
381 glyphsetCache[prev_best].next = glyphsetCache[best].next;
382 glyphsetCache[best].next = mru;
383 mru = best;
384 } else {
385 assert(mru == best);
387 return mru;
390 TRACE("Growing cache\n");
392 if (glyphsetCache)
393 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
394 glyphsetCache,
395 (glyphsetCacheSize + INIT_CACHE_SIZE)
396 * sizeof(*glyphsetCache));
397 else
398 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
399 (glyphsetCacheSize + INIT_CACHE_SIZE)
400 * sizeof(*glyphsetCache));
402 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
403 i++) {
404 glyphsetCache[i].next = i + 1;
405 glyphsetCache[i].count = -1;
407 glyphsetCache[i-1].next = -1;
408 glyphsetCacheSize += INIT_CACHE_SIZE;
410 lastfree = glyphsetCache[best].next;
411 glyphsetCache[best].count = 1;
412 glyphsetCache[best].next = mru;
413 mru = best;
414 TRACE("new free cache slot at %d\n", mru);
415 return mru;
418 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
420 DWORD size;
421 WORD *gasp, *buffer;
422 WORD num_recs;
423 DWORD ppem;
424 TEXTMETRICW tm;
426 *flags = 0;
428 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
429 if(size == GDI_ERROR)
430 return FALSE;
432 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
433 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
435 GetTextMetricsW(physDev->hdc, &tm);
436 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
438 gasp++;
439 num_recs = get_be_word(*gasp);
440 gasp++;
441 while(num_recs--)
443 *flags = get_be_word(*(gasp + 1));
444 if(ppem <= get_be_word(*gasp))
445 break;
446 gasp += 2;
448 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
450 HeapFree(GetProcessHeap(), 0, buffer);
451 return TRUE;
454 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
456 int ret;
457 int format;
458 gsCacheEntry *entry;
459 WORD flags;
460 static int hinter = -1;
461 static int subpixel = -1;
463 if((ret = LookupEntry(plfsz)) != -1) return ret;
465 ret = AllocEntry();
466 entry = glyphsetCache + ret;
467 entry->lfsz = *plfsz;
468 for( format = 0; format < AA_MAXVALUE; format++ ) {
469 assert( !entry->format[format] );
472 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
474 if(hinter == -1 || subpixel == -1)
476 RASTERIZER_STATUS status;
477 GetRasterizerCaps(&status, sizeof(status));
478 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
479 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
482 /* FIXME: Use the following registry information
483 [HKEY_CURRENT_USER\Control Panel\Desktop]
484 "FontSmoothing"="2" ; 0=>Off, 2=>On
485 "FontSmoothingType"=dword:00000002 ; 1=>Standard, 2=>Cleartype
486 "FontSmoothingOrientation"=dword:00000001 ; 0=>BGR, 1=>RGB
487 "FontSmoothingGamma"=dword:00000578
489 if ( subpixel && X11DRV_XRender_Installed)
490 entry->aa_default = AA_RGB;
491 else if(!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
492 entry->aa_default = AA_Grey;
493 else
494 entry->aa_default = AA_None;
496 else
497 entry->aa_default = AA_None;
499 return ret;
502 static void dec_ref_cache(int index)
504 assert(index >= 0);
505 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
506 assert(glyphsetCache[index].count > 0);
507 glyphsetCache[index].count--;
510 static void lfsz_calc_hash(LFANDSIZE *plfsz)
512 DWORD hash = 0, *ptr;
513 int i;
515 hash ^= plfsz->devsize.cx;
516 hash ^= plfsz->devsize.cy;
517 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
518 hash ^= *ptr;
519 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
520 WCHAR *pwc = (WCHAR *)ptr;
521 if(!*pwc) break;
522 hash ^= *ptr;
523 pwc++;
524 if(!*pwc) break;
526 plfsz->hash = hash;
527 return;
530 /***********************************************************************
531 * X11DRV_XRender_Finalize
533 void X11DRV_XRender_Finalize(void)
535 int i;
537 EnterCriticalSection(&xrender_cs);
538 for(i = mru; i >= 0; i = glyphsetCache[i].next)
539 FreeEntry(i);
540 LeaveCriticalSection(&xrender_cs);
544 /***********************************************************************
545 * X11DRV_XRender_SelectFont
547 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
549 LFANDSIZE lfsz;
551 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
552 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
553 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
554 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
555 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
556 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
557 lfsz_calc_hash(&lfsz);
559 EnterCriticalSection(&xrender_cs);
560 if(!physDev->xrender) {
561 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
562 sizeof(*physDev->xrender));
563 physDev->xrender->cache_index = -1;
565 else if(physDev->xrender->cache_index != -1)
566 dec_ref_cache(physDev->xrender->cache_index);
567 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
568 LeaveCriticalSection(&xrender_cs);
569 return 0;
572 /***********************************************************************
573 * X11DRV_XRender_DeleteDC
575 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
577 X11DRV_XRender_UpdateDrawable(physDev);
579 EnterCriticalSection(&xrender_cs);
580 if(physDev->xrender->cache_index != -1)
581 dec_ref_cache(physDev->xrender->cache_index);
582 LeaveCriticalSection(&xrender_cs);
584 HeapFree(GetProcessHeap(), 0, physDev->xrender);
585 physDev->xrender = NULL;
586 return;
589 /***********************************************************************
590 * X11DRV_XRender_UpdateDrawable
592 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
593 * It deletes the pict and tile when the drawable changes.
595 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
597 wine_tsx11_lock();
599 if(physDev->xrender->pict)
601 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
602 XFlush(gdi_display);
603 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
604 physDev->xrender->pict = 0;
606 wine_tsx11_unlock();
608 return;
611 /************************************************************************
612 * UploadGlyph
614 * Helper to ExtTextOut. Must be called inside xrender_cs
616 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
618 unsigned int buflen;
619 char *buf;
620 Glyph gid;
621 GLYPHMETRICS gm;
622 XGlyphInfo gi;
623 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
624 gsCacheEntryFormat *formatEntry;
625 UINT ggo_format = GGO_GLYPH_INDEX;
626 XRenderPictFormat pf;
627 unsigned long pf_mask;
628 static const char zero[4];
630 switch(format) {
631 case AA_Grey:
632 ggo_format |= WINE_GGO_GRAY16_BITMAP;
633 break;
634 case AA_RGB:
635 ggo_format |= WINE_GGO_HRGB_BITMAP;
636 break;
637 case AA_BGR:
638 ggo_format |= WINE_GGO_HBGR_BITMAP;
639 break;
640 case AA_VRGB:
641 ggo_format |= WINE_GGO_VRGB_BITMAP;
642 break;
643 case AA_VBGR:
644 ggo_format |= WINE_GGO_VBGR_BITMAP;
645 break;
647 default:
648 ERR("aa = %d - not implemented\n", format);
649 case AA_None:
650 ggo_format |= GGO_BITMAP;
651 break;
654 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
655 NULL);
656 if(buflen == GDI_ERROR) {
657 if(format != AA_None) {
658 format = AA_None;
659 entry->aa_default = AA_None;
660 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
661 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
662 NULL);
664 if(buflen == GDI_ERROR) {
665 WARN("GetGlyphOutlineW failed\n");
666 return FALSE;
668 TRACE("Turning off antialiasing for this monochrome font\n");
671 /* If there is nothing for the current type, we create the entry. */
672 if( !entry->format[format] ) {
673 entry->format[format] = HeapAlloc(GetProcessHeap(),
674 HEAP_ZERO_MEMORY,
675 sizeof(gsCacheEntryFormat));
677 formatEntry = entry->format[format];
679 if(formatEntry->nrealized <= glyph) {
680 formatEntry->nrealized = (glyph / 128 + 1) * 128;
682 if (formatEntry->realized)
683 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
684 HEAP_ZERO_MEMORY,
685 formatEntry->realized,
686 formatEntry->nrealized * sizeof(BOOL));
687 else
688 formatEntry->realized = HeapAlloc(GetProcessHeap(),
689 HEAP_ZERO_MEMORY,
690 formatEntry->nrealized * sizeof(BOOL));
692 if(!X11DRV_XRender_Installed) {
693 if (formatEntry->bitmaps)
694 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
695 HEAP_ZERO_MEMORY,
696 formatEntry->bitmaps,
697 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
698 else
699 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
700 HEAP_ZERO_MEMORY,
701 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
703 if (formatEntry->gis)
704 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
705 HEAP_ZERO_MEMORY,
706 formatEntry->gis,
707 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
708 else
709 formatEntry->gis = HeapAlloc(GetProcessHeap(),
710 HEAP_ZERO_MEMORY,
711 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
715 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
716 switch(format) {
717 case AA_Grey:
718 pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
719 pf.type = PictTypeDirect;
720 pf.depth = 8;
721 pf.direct.alpha = 0;
722 pf.direct.alphaMask = 0xff;
723 break;
725 case AA_RGB:
726 case AA_BGR:
727 case AA_VRGB:
728 case AA_VBGR:
729 pf_mask = PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask |
730 PictFormatGreen | PictFormatGreenMask | PictFormatBlue |
731 PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask;
732 pf.type = PictTypeDirect;
733 pf.depth = 32;
734 pf.direct.red = 16;
735 pf.direct.redMask = 0xff;
736 pf.direct.green = 8;
737 pf.direct.greenMask = 0xff;
738 pf.direct.blue = 0;
739 pf.direct.blueMask = 0xff;
740 pf.direct.alpha = 24;
741 pf.direct.alphaMask = 0xff;
742 break;
744 default:
745 ERR("aa = %d - not implemented\n", format);
746 case AA_None:
747 pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
748 pf.type = PictTypeDirect;
749 pf.depth = 1;
750 pf.direct.alpha = 0;
751 pf.direct.alphaMask = 1;
752 break;
755 wine_tsx11_lock();
756 formatEntry->font_format = pXRenderFindFormat(gdi_display, pf_mask, &pf, 0);
757 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
758 wine_tsx11_unlock();
762 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
763 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, NULL);
764 formatEntry->realized[glyph] = TRUE;
766 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
767 buflen,
768 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
769 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
771 gi.width = gm.gmBlackBoxX;
772 gi.height = gm.gmBlackBoxY;
773 gi.x = -gm.gmptGlyphOrigin.x;
774 gi.y = gm.gmptGlyphOrigin.y;
775 gi.xOff = gm.gmCellIncX;
776 gi.yOff = gm.gmCellIncY;
778 if(TRACE_ON(xrender)) {
779 int pitch, i, j;
780 char output[300];
781 unsigned char *line;
783 if(format == AA_None) {
784 pitch = ((gi.width + 31) / 32) * 4;
785 for(i = 0; i < gi.height; i++) {
786 line = (unsigned char*) buf + i * pitch;
787 output[0] = '\0';
788 for(j = 0; j < pitch * 8; j++) {
789 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
791 TRACE("%s\n", output);
793 } else {
794 static const char blks[] = " .:;!o*#";
795 char str[2];
797 str[1] = '\0';
798 pitch = ((gi.width + 3) / 4) * 4;
799 for(i = 0; i < gi.height; i++) {
800 line = (unsigned char*) buf + i * pitch;
801 output[0] = '\0';
802 for(j = 0; j < pitch; j++) {
803 str[0] = blks[line[j] >> 5];
804 strcat(output, str);
806 TRACE("%s\n", output);
812 if(formatEntry->glyphset) {
813 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
814 unsigned char *byte = (unsigned char*) buf, c;
815 int i = buflen;
817 while(i--) {
818 c = *byte;
820 /* magic to flip bit order */
821 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
822 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
823 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
825 *byte++ = c;
828 else if ( format != AA_Grey &&
829 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
831 unsigned int i, *data = (unsigned int *)buf;
832 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
834 gid = glyph;
837 XRenderCompositeText seems to ignore 0x0 glyphs when
838 AA_None, which means we lose the advance width of glyphs
839 like the space. We'll pretend that such glyphs are 1x1
840 bitmaps.
843 if(buflen == 0)
844 gi.width = gi.height = 1;
846 wine_tsx11_lock();
847 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
848 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
849 wine_tsx11_unlock();
850 HeapFree(GetProcessHeap(), 0, buf);
851 } else {
852 formatEntry->bitmaps[glyph] = buf;
855 formatEntry->gis[glyph] = gi;
857 return TRUE;
860 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
861 void *bitmap, XGlyphInfo *gi)
863 unsigned char *srcLine = bitmap, *src;
864 unsigned char bits, bitsMask;
865 int width = gi->width;
866 int stride = ((width + 31) & ~31) >> 3;
867 int height = gi->height;
868 int w;
869 int xspan, lenspan;
871 TRACE("%d, %d\n", x, y);
872 x -= gi->x;
873 y -= gi->y;
874 while (height--)
876 src = srcLine;
877 srcLine += stride;
878 w = width;
880 bitsMask = 0x80; /* FreeType is always MSB first */
881 bits = *src++;
883 xspan = x;
884 while (w)
886 if (bits & bitsMask)
888 lenspan = 0;
891 lenspan++;
892 if (lenspan == w)
893 break;
894 bitsMask = bitsMask >> 1;
895 if (!bitsMask)
897 bits = *src++;
898 bitsMask = 0x80;
900 } while (bits & bitsMask);
901 XFillRectangle (gdi_display, physDev->drawable,
902 physDev->gc, xspan, y, lenspan, 1);
903 xspan += lenspan;
904 w -= lenspan;
906 else
910 w--;
911 xspan++;
912 if (!w)
913 break;
914 bitsMask = bitsMask >> 1;
915 if (!bitsMask)
917 bits = *src++;
918 bitsMask = 0x80;
920 } while (!(bits & bitsMask));
923 y++;
927 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
928 void *bitmap, XGlyphInfo *gi)
930 unsigned char *srcLine = bitmap, *src, bits;
931 int width = gi->width;
932 int stride = ((width + 3) & ~3);
933 int height = gi->height;
934 int w;
935 int xspan, lenspan;
937 x -= gi->x;
938 y -= gi->y;
939 while (height--)
941 src = srcLine;
942 srcLine += stride;
943 w = width;
945 bits = *src++;
946 xspan = x;
947 while (w)
949 if (bits >= 0x80)
951 lenspan = 0;
954 lenspan++;
955 if (lenspan == w)
956 break;
957 bits = *src++;
958 } while (bits >= 0x80);
959 XFillRectangle (gdi_display, physDev->drawable,
960 physDev->gc, xspan, y, lenspan, 1);
961 xspan += lenspan;
962 w -= lenspan;
964 else
968 w--;
969 xspan++;
970 if (!w)
971 break;
972 bits = *src++;
973 } while (bits < 0x80);
976 y++;
981 static void ExamineBitfield (DWORD mask, int *shift, int *len)
983 int s, l;
985 s = 0;
986 while ((mask & 1) == 0)
988 mask >>= 1;
989 s++;
991 l = 0;
992 while ((mask & 1) == 1)
994 mask >>= 1;
995 l++;
997 *shift = s;
998 *len = l;
1001 static DWORD GetField (DWORD pixel, int shift, int len)
1003 pixel = pixel & (((1 << (len)) - 1) << shift);
1004 pixel = pixel << (32 - (shift + len)) >> 24;
1005 while (len < 8)
1007 pixel |= (pixel >> len);
1008 len <<= 1;
1010 return pixel;
1014 static DWORD PutField (DWORD pixel, int shift, int len)
1016 shift = shift - (8 - len);
1017 if (len <= 8)
1018 pixel &= (((1 << len) - 1) << (8 - len));
1019 if (shift < 0)
1020 pixel >>= -shift;
1021 else
1022 pixel <<= shift;
1023 return pixel;
1026 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1027 int color)
1029 int r_shift, r_len;
1030 int g_shift, g_len;
1031 int b_shift, b_len;
1032 BYTE *maskLine, *mask, m;
1033 int maskStride;
1034 DWORD pixel;
1035 int width, height;
1036 int w, tx;
1037 BYTE src_r, src_g, src_b;
1039 x -= gi->x;
1040 y -= gi->y;
1041 width = gi->width;
1042 height = gi->height;
1044 maskLine = (unsigned char *) bitmap;
1045 maskStride = (width + 3) & ~3;
1047 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1048 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1049 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1051 src_r = GetField(color, r_shift, r_len);
1052 src_g = GetField(color, g_shift, g_len);
1053 src_b = GetField(color, b_shift, b_len);
1055 for(; height--; y++)
1057 mask = maskLine;
1058 maskLine += maskStride;
1059 w = width;
1060 tx = x;
1062 if(y < 0) continue;
1063 if(y >= image->height) break;
1065 for(; w--; tx++)
1067 if(tx >= image->width) break;
1069 m = *mask++;
1070 if(tx < 0) continue;
1072 if (m == 0xff)
1073 XPutPixel (image, tx, y, color);
1074 else if (m)
1076 BYTE r, g, b;
1078 pixel = XGetPixel (image, tx, y);
1080 r = GetField(pixel, r_shift, r_len);
1081 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1082 g = GetField(pixel, g_shift, g_len);
1083 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1084 b = GetField(pixel, b_shift, b_len);
1085 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1087 pixel = (PutField (r, r_shift, r_len) |
1088 PutField (g, g_shift, g_len) |
1089 PutField (b, b_shift, b_len));
1090 XPutPixel (image, tx, y, pixel);
1096 /*************************************************************
1097 * get_tile_pict
1099 * Returns an appropriate Picture for tiling the text colour.
1100 * Call and use result within the xrender_cs
1102 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1104 static struct
1106 Pixmap xpm;
1107 Picture pict;
1108 int current_color;
1109 } tiles[2], *tile;
1110 XRenderColor col;
1112 tile = &tiles[type];
1114 if(!tile->xpm)
1116 XRenderPictureAttributes pa;
1118 wine_tsx11_lock();
1119 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1121 pa.repeat = True;
1122 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1123 wine_tsx11_unlock();
1125 /* init current_color to something different from text_pixel */
1126 tile->current_color = ~text_pixel;
1128 if(type == mono_drawable)
1130 /* for a 1bpp bitmap we always need a 1 in the tile */
1131 col.red = col.green = col.blue = 0;
1132 col.alpha = 0xffff;
1133 wine_tsx11_lock();
1134 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1135 wine_tsx11_unlock();
1139 if(text_pixel != tile->current_color && type == color_drawable)
1141 /* Map 0 -- 0xff onto 0 -- 0xffff */
1142 int r_shift, r_len;
1143 int g_shift, g_len;
1144 int b_shift, b_len;
1146 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1147 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1148 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1150 col.red = GetField(text_pixel, r_shift, r_len);
1151 col.red |= col.red << 8;
1152 col.green = GetField(text_pixel, g_shift, g_len);
1153 col.green |= col.green << 8;
1154 col.blue = GetField(text_pixel, b_shift, b_len);
1155 col.blue |= col.blue << 8;
1156 col.alpha = 0xffff;
1158 wine_tsx11_lock();
1159 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1160 wine_tsx11_unlock();
1161 tile->current_color = text_pixel;
1163 return tile->pict;
1166 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1168 return 1;
1171 /***********************************************************************
1172 * X11DRV_XRender_ExtTextOut
1174 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1175 const RECT *lprect, LPCWSTR wstr, UINT count,
1176 const INT *lpDx )
1178 RGNDATA *data;
1179 XGCValues xgcval;
1180 gsCacheEntry *entry;
1181 gsCacheEntryFormat *formatEntry;
1182 BOOL retv = FALSE;
1183 HDC hdc = physDev->hdc;
1184 int textPixel, backgroundPixel;
1185 HRGN saved_region = 0;
1186 BOOL disable_antialias = FALSE;
1187 AA_Type aa_type = AA_None;
1188 DIBSECTION bmp;
1189 unsigned int idx;
1190 double cosEsc, sinEsc;
1191 LOGFONTW lf;
1192 enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1193 Picture tile_pict = 0;
1195 /* Do we need to disable antialiasing because of palette mode? */
1196 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1197 TRACE("bitmap is not a DIB\n");
1199 else if (bmp.dsBmih.biBitCount <= 8) {
1200 TRACE("Disabling antialiasing\n");
1201 disable_antialias = TRUE;
1204 xgcval.function = GXcopy;
1205 xgcval.background = physDev->backgroundPixel;
1206 xgcval.fill_style = FillSolid;
1207 wine_tsx11_lock();
1208 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1209 wine_tsx11_unlock();
1211 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1213 if(physDev->depth == 1) {
1214 if((physDev->textPixel & 0xffffff) == 0) {
1215 textPixel = 0;
1216 backgroundPixel = 1;
1217 } else {
1218 textPixel = 1;
1219 backgroundPixel = 0;
1221 } else {
1222 textPixel = physDev->textPixel;
1223 backgroundPixel = physDev->backgroundPixel;
1226 if(flags & ETO_OPAQUE)
1228 wine_tsx11_lock();
1229 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1230 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1231 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1232 lprect->right - lprect->left, lprect->bottom - lprect->top );
1233 wine_tsx11_unlock();
1236 if(count == 0)
1238 retv = TRUE;
1239 goto done_unlock;
1243 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1244 if(lf.lfEscapement != 0) {
1245 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1246 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1247 } else {
1248 cosEsc = 1;
1249 sinEsc = 0;
1252 if (flags & ETO_CLIPPED)
1254 HRGN clip_region;
1256 clip_region = CreateRectRgnIndirect( lprect );
1257 /* make a copy of the current device region */
1258 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1259 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1260 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1261 DeleteObject( clip_region );
1264 if(X11DRV_XRender_Installed) {
1265 if(!physDev->xrender->pict) {
1266 XRenderPictureAttributes pa;
1267 pa.subwindow_mode = IncludeInferiors;
1269 wine_tsx11_lock();
1270 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1271 physDev->drawable,
1272 pict_formats[depth_type],
1273 CPSubwindowMode, &pa);
1274 wine_tsx11_unlock();
1276 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1277 physDev->xrender->pict, hdc, physDev->drawable);
1278 } else {
1279 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1280 physDev->xrender->pict, hdc, physDev->drawable);
1283 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1285 wine_tsx11_lock();
1286 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1287 physDev->dc_rect.left, physDev->dc_rect.top,
1288 (XRectangle *)data->Buffer, data->rdh.nCount );
1289 wine_tsx11_unlock();
1290 HeapFree( GetProcessHeap(), 0, data );
1294 EnterCriticalSection(&xrender_cs);
1296 entry = glyphsetCache + physDev->xrender->cache_index;
1297 if( disable_antialias == FALSE )
1298 aa_type = entry->aa_default;
1299 formatEntry = entry->format[aa_type];
1301 for(idx = 0; idx < count; idx++) {
1302 if( !formatEntry ) {
1303 UploadGlyph(physDev, wstr[idx], aa_type);
1304 /* re-evaluate antialias since aa_default may have changed */
1305 if( disable_antialias == FALSE )
1306 aa_type = entry->aa_default;
1307 formatEntry = entry->format[aa_type];
1308 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1309 UploadGlyph(physDev, wstr[idx], aa_type);
1312 if (!formatEntry)
1314 WARN("could not upload requested glyphs\n");
1315 LeaveCriticalSection(&xrender_cs);
1316 goto done_unlock;
1319 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1320 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1322 if(X11DRV_XRender_Installed)
1324 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1325 INT offset = 0;
1326 POINT desired, current;
1327 int render_op = PictOpOver;
1329 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1330 So we pass zeros to the function and move to our starting position using the first
1331 element of the elts array. */
1333 desired.x = physDev->dc_rect.left + x;
1334 desired.y = physDev->dc_rect.top + y;
1335 current.x = current.y = 0;
1337 tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1339 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1341 if((depth_type == mono_drawable) && (textPixel == 0))
1342 render_op = PictOpOutReverse; /* This gives us 'black' text */
1344 for(idx = 0; idx < count; idx++)
1346 elts[idx].glyphset = formatEntry->glyphset;
1347 elts[idx].chars = wstr + idx;
1348 elts[idx].nchars = 1;
1349 elts[idx].xOff = desired.x - current.x;
1350 elts[idx].yOff = desired.y - current.y;
1352 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1353 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1355 if(!lpDx)
1357 desired.x += formatEntry->gis[wstr[idx]].xOff;
1358 desired.y += formatEntry->gis[wstr[idx]].yOff;
1360 else
1362 offset += lpDx[idx];
1363 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1364 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1367 wine_tsx11_lock();
1368 pXRenderCompositeText16(gdi_display, render_op,
1369 tile_pict,
1370 physDev->xrender->pict,
1371 formatEntry->font_format,
1372 0, 0, 0, 0, elts, count);
1373 wine_tsx11_unlock();
1374 HeapFree(GetProcessHeap(), 0, elts);
1375 } else {
1376 INT offset = 0, xoff = 0, yoff = 0;
1377 wine_tsx11_lock();
1378 XSetForeground( gdi_display, physDev->gc, textPixel );
1380 if(aa_type == AA_None || physDev->depth == 1)
1382 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1384 if(aa_type == AA_None)
1385 sharp_glyph_fn = SharpGlyphMono;
1386 else
1387 sharp_glyph_fn = SharpGlyphGray;
1389 for(idx = 0; idx < count; idx++) {
1390 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1391 physDev->dc_rect.top + y + yoff,
1392 formatEntry->bitmaps[wstr[idx]],
1393 &formatEntry->gis[wstr[idx]]);
1394 if(lpDx) {
1395 offset += lpDx[idx];
1396 xoff = offset * cosEsc;
1397 yoff = offset * -sinEsc;
1398 } else {
1399 xoff += formatEntry->gis[wstr[idx]].xOff;
1400 yoff += formatEntry->gis[wstr[idx]].yOff;
1403 } else {
1404 XImage *image;
1405 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1406 RECT extents = {0, 0, 0, 0};
1407 POINT cur = {0, 0};
1408 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1409 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1411 TRACE("drawable %dx%d\n", w, h);
1413 for(idx = 0; idx < count; idx++) {
1414 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1415 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1416 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1417 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1418 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1419 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1420 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1421 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1422 if(lpDx) {
1423 offset += lpDx[idx];
1424 cur.x = offset * cosEsc;
1425 cur.y = offset * -sinEsc;
1426 } else {
1427 cur.x += formatEntry->gis[wstr[idx]].xOff;
1428 cur.y += formatEntry->gis[wstr[idx]].yOff;
1431 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1432 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1434 if(physDev->dc_rect.left + x + extents.left >= 0) {
1435 image_x = physDev->dc_rect.left + x + extents.left;
1436 image_off_x = 0;
1437 } else {
1438 image_x = 0;
1439 image_off_x = physDev->dc_rect.left + x + extents.left;
1441 if(physDev->dc_rect.top + y + extents.top >= 0) {
1442 image_y = physDev->dc_rect.top + y + extents.top;
1443 image_off_y = 0;
1444 } else {
1445 image_y = 0;
1446 image_off_y = physDev->dc_rect.top + y + extents.top;
1448 if(physDev->dc_rect.left + x + extents.right < w)
1449 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1450 else
1451 image_w = w - image_x;
1452 if(physDev->dc_rect.top + y + extents.bottom < h)
1453 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1454 else
1455 image_h = h - image_y;
1457 if(image_w <= 0 || image_h <= 0) goto no_image;
1459 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1460 image = XGetImage(gdi_display, physDev->drawable,
1461 image_x, image_y, image_w, image_h,
1462 AllPlanes, ZPixmap);
1463 X11DRV_check_error();
1465 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1466 gdi_display, (int)physDev->drawable, image_x, image_y,
1467 image_w, image_h, AllPlanes, ZPixmap,
1468 physDev->depth, image);
1469 if(!image) {
1470 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1471 physDev->depth);
1472 GC gc;
1473 XGCValues gcv;
1475 gcv.graphics_exposures = False;
1476 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1477 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1478 image_w, image_h, 0, 0);
1479 XFreeGC(gdi_display, gc);
1480 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1481 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1482 ZPixmap);
1483 X11DRV_check_error();
1484 XFreePixmap(gdi_display, xpm);
1486 if(!image) goto no_image;
1488 image->red_mask = visual->red_mask;
1489 image->green_mask = visual->green_mask;
1490 image->blue_mask = visual->blue_mask;
1492 offset = xoff = yoff = 0;
1493 for(idx = 0; idx < count; idx++) {
1494 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1495 yoff + image_off_y - extents.top,
1496 formatEntry->bitmaps[wstr[idx]],
1497 &formatEntry->gis[wstr[idx]],
1498 physDev->textPixel);
1499 if(lpDx) {
1500 offset += lpDx[idx];
1501 xoff = offset * cosEsc;
1502 yoff = offset * -sinEsc;
1503 } else {
1504 xoff += formatEntry->gis[wstr[idx]].xOff;
1505 yoff += formatEntry->gis[wstr[idx]].yOff;
1508 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1509 image_x, image_y, image_w, image_h);
1510 XDestroyImage(image);
1512 no_image:
1513 wine_tsx11_unlock();
1515 LeaveCriticalSection(&xrender_cs);
1517 if (flags & ETO_CLIPPED)
1519 /* restore the device region */
1520 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1521 DeleteObject( saved_region );
1524 retv = TRUE;
1526 done_unlock:
1527 X11DRV_UnlockDIBSection( physDev, TRUE );
1528 return retv;
1531 /******************************************************************************
1532 * AlphaBlend (x11drv.@)
1534 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1535 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1536 BLENDFUNCTION blendfn)
1538 XRenderPictureAttributes pa;
1539 XRenderPictFormat *src_format;
1540 XRenderPictFormat argb32_templ = {
1541 0, /* id */
1542 PictTypeDirect, /* type */
1543 32, /* depth */
1544 { /* direct */
1545 16, /* direct.red */
1546 0xff, /* direct.redMask */
1547 8, /* direct.green */
1548 0xff, /* direct.greenMask */
1549 0, /* direct.blue */
1550 0xff, /* direct.blueMask */
1551 24, /* direct.alpha */
1552 0xff, /* direct.alphaMask */
1554 0, /* colormap */
1556 unsigned long argb32_templ_mask =
1557 PictFormatType |
1558 PictFormatDepth |
1559 PictFormatRed |
1560 PictFormatRedMask |
1561 PictFormatGreen |
1562 PictFormatGreenMask |
1563 PictFormatBlue |
1564 PictFormatBlueMask |
1565 PictFormatAlpha |
1566 PictFormatAlphaMask;
1568 Picture dst_pict, src_pict;
1569 Pixmap xpm;
1570 DIBSECTION dib;
1571 XImage *image;
1572 GC gc;
1573 XGCValues gcv;
1574 DWORD *dstbits, *data;
1575 int y, y2;
1576 POINT pts[2];
1577 BOOL top_down = FALSE;
1578 RGNDATA *rgndata;
1579 enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1581 if(!X11DRV_XRender_Installed) {
1582 FIXME("Unable to AlphaBlend without Xrender\n");
1583 return FALSE;
1585 pts[0].x = xDst;
1586 pts[0].y = yDst;
1587 pts[1].x = xDst + widthDst;
1588 pts[1].y = yDst + heightDst;
1589 LPtoDP(devDst->hdc, pts, 2);
1590 xDst = pts[0].x;
1591 yDst = pts[0].y;
1592 widthDst = pts[1].x - pts[0].x;
1593 heightDst = pts[1].y - pts[0].y;
1595 pts[0].x = xSrc;
1596 pts[0].y = ySrc;
1597 pts[1].x = xSrc + widthSrc;
1598 pts[1].y = ySrc + heightSrc;
1599 LPtoDP(devSrc->hdc, pts, 2);
1600 xSrc = pts[0].x;
1601 ySrc = pts[0].y;
1602 widthSrc = pts[1].x - pts[0].x;
1603 heightSrc = pts[1].y - pts[0].y;
1604 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1606 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1607 if(widthDst != widthSrc || heightDst != heightSrc)
1608 #else
1609 if(!pXRenderSetPictureTransform)
1610 #endif
1612 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1613 return FALSE;
1616 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1618 static BOOL out = FALSE;
1619 if (!out)
1621 FIXME("not a dibsection\n");
1622 out = TRUE;
1624 return FALSE;
1627 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1628 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1630 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1631 SetLastError(ERROR_INVALID_PARAMETER);
1632 return FALSE;
1635 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1636 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1638 if(dib.dsBm.bmBitsPixel != 32) {
1639 FIXME("not a 32 bpp dibsection\n");
1640 return FALSE;
1642 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1644 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1645 top_down = TRUE;
1646 dstbits += widthSrc * (heightSrc - 1);
1647 y2 = ySrc;
1648 y = y2 + heightSrc - 1;
1650 else
1652 y = dib.dsBmih.biHeight - ySrc - 1;
1653 y2 = y - heightSrc + 1;
1656 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1658 for(; y >= y2; y--)
1660 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1661 widthSrc * 4);
1662 dstbits += (top_down ? -1 : 1) * widthSrc;
1665 else
1667 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1668 int x;
1670 for(; y >= y2; y--)
1672 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1673 for (x = 0; x < widthSrc; x++)
1675 DWORD argb = *srcbits++;
1676 argb = (argb & 0xffffff) | source_alpha;
1677 *dstbits++ = argb;
1679 if (top_down) /* we traversed the row forward so we should go back by two rows */
1680 dstbits -= 2 * widthSrc;
1685 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1687 wine_tsx11_lock();
1688 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1689 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1692 Avoid using XRenderFindStandardFormat as older libraries don't have it
1693 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1695 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1697 TRACE("src_format %p\n", src_format);
1699 pa.subwindow_mode = IncludeInferiors;
1701 /* FIXME use devDst->xrender->pict ? */
1702 dst_pict = pXRenderCreatePicture(gdi_display,
1703 devDst->drawable,
1704 pict_formats[dst_depth_type],
1705 CPSubwindowMode, &pa);
1706 TRACE("dst_pict %08lx\n", dst_pict);
1707 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1708 xpm = XCreatePixmap(gdi_display,
1709 root_window,
1710 widthSrc, heightSrc, 32);
1711 gcv.graphics_exposures = False;
1712 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1713 TRACE("xpm = %08lx\n", xpm);
1714 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1716 src_pict = pXRenderCreatePicture(gdi_display,
1717 xpm, src_format,
1718 CPSubwindowMode, &pa);
1719 TRACE("src_pict %08lx\n", src_pict);
1721 if (rgndata)
1723 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1724 devDst->dc_rect.left, devDst->dc_rect.top,
1725 (XRectangle *)rgndata->Buffer,
1726 rgndata->rdh.nCount );
1727 HeapFree( GetProcessHeap(), 0, rgndata );
1730 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1731 if(widthDst != widthSrc || heightDst != heightSrc) {
1732 double xscale = widthSrc/(double)widthDst;
1733 double yscale = heightSrc/(double)heightDst;
1734 XTransform xform = {{
1735 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1736 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1737 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1739 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1741 #endif
1742 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1743 0, 0, 0, 0,
1744 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1747 pXRenderFreePicture(gdi_display, src_pict);
1748 XFreePixmap(gdi_display, xpm);
1749 XFreeGC(gdi_display, gc);
1750 pXRenderFreePicture(gdi_display, dst_pict);
1751 image->data = NULL;
1752 XDestroyImage(image);
1754 wine_tsx11_unlock();
1755 HeapFree(GetProcessHeap(), 0, data);
1756 return TRUE;
1759 #else /* SONAME_LIBXRENDER */
1761 void X11DRV_XRender_Init(void)
1763 TRACE("XRender support not compiled in.\n");
1764 return;
1767 void X11DRV_XRender_Finalize(void)
1771 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1773 assert(0);
1774 return FALSE;
1777 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1779 assert(0);
1780 return;
1783 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1784 const RECT *lprect, LPCWSTR wstr, UINT count,
1785 const INT *lpDx )
1787 assert(0);
1788 return FALSE;
1791 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1793 assert(0);
1794 return;
1797 /******************************************************************************
1798 * AlphaBlend (x11drv.@)
1800 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1801 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1802 BLENDFUNCTION blendfn)
1804 FIXME("not supported - XRENDER headers were missing at compile time\n");
1805 return FALSE;
1808 #endif /* SONAME_LIBXRENDER */