gdiplus/tests: Added pen dash array tests.
[wine.git] / dlls / winex11.drv / xrender.c
blob8511f45626881187eb082cedf02c2ad408ed2d71
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 static BOOL X11DRV_XRender_Installed = FALSE;
40 int using_client_side_fonts = FALSE;
42 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
44 #ifdef SONAME_LIBXRENDER
46 #include <X11/Xlib.h>
47 #include <X11/extensions/Xrender.h>
50 enum drawable_depth_type {mono_drawable, color_drawable};
51 static XRenderPictFormat *pict_formats[2];
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;
90 static gsCacheEntry *glyphsetCache = NULL;
91 static DWORD glyphsetCacheSize = 0;
92 static INT lastfree = -1;
93 static INT mru = -1;
95 #define INIT_CACHE_SIZE 10
97 static int antialias = 1;
99 static void *xrender_handle;
101 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
102 MAKE_FUNCPTR(XRenderAddGlyphs)
103 MAKE_FUNCPTR(XRenderComposite)
104 MAKE_FUNCPTR(XRenderCompositeString8)
105 MAKE_FUNCPTR(XRenderCompositeString16)
106 MAKE_FUNCPTR(XRenderCompositeString32)
107 MAKE_FUNCPTR(XRenderCompositeText16)
108 MAKE_FUNCPTR(XRenderCreateGlyphSet)
109 MAKE_FUNCPTR(XRenderCreatePicture)
110 MAKE_FUNCPTR(XRenderFillRectangle)
111 MAKE_FUNCPTR(XRenderFindFormat)
112 MAKE_FUNCPTR(XRenderFindVisualFormat)
113 MAKE_FUNCPTR(XRenderFreeGlyphSet)
114 MAKE_FUNCPTR(XRenderFreePicture)
115 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
116 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
117 MAKE_FUNCPTR(XRenderSetPictureTransform)
118 #endif
119 MAKE_FUNCPTR(XRenderQueryExtension)
120 #undef MAKE_FUNCPTR
122 static CRITICAL_SECTION xrender_cs;
123 static CRITICAL_SECTION_DEBUG critsect_debug =
125 0, 0, &xrender_cs,
126 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
127 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
129 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
131 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
132 ( ( (ULONG)_x4 << 24 ) | \
133 ( (ULONG)_x3 << 16 ) | \
134 ( (ULONG)_x2 << 8 ) | \
135 (ULONG)_x1 )
137 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
139 #define GASP_GRIDFIT 0x01
140 #define GASP_DOGRAY 0x02
142 #ifdef WORDS_BIGENDIAN
143 #define get_be_word(x) (x)
144 #else
145 #define get_be_word(x) RtlUshortByteSwap(x)
146 #endif
148 /***********************************************************************
149 * X11DRV_XRender_Init
151 * Let's see if our XServer has the extension available
154 void X11DRV_XRender_Init(void)
156 int event_base, i;
157 XRenderPictFormat pf;
159 if (client_side_with_render &&
160 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
161 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
162 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
165 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
166 LOAD_FUNCPTR(XRenderAddGlyphs)
167 LOAD_FUNCPTR(XRenderComposite)
168 LOAD_FUNCPTR(XRenderCompositeString8)
169 LOAD_FUNCPTR(XRenderCompositeString16)
170 LOAD_FUNCPTR(XRenderCompositeString32)
171 LOAD_FUNCPTR(XRenderCompositeText16)
172 LOAD_FUNCPTR(XRenderCreateGlyphSet)
173 LOAD_FUNCPTR(XRenderCreatePicture)
174 LOAD_FUNCPTR(XRenderFillRectangle)
175 LOAD_FUNCPTR(XRenderFindFormat)
176 LOAD_FUNCPTR(XRenderFindVisualFormat)
177 LOAD_FUNCPTR(XRenderFreeGlyphSet)
178 LOAD_FUNCPTR(XRenderFreePicture)
179 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
180 LOAD_FUNCPTR(XRenderQueryExtension)
181 #undef LOAD_FUNCPTR
182 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
183 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
184 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
185 #undef LOAD_OPTIONAL_FUNCPTR
186 #endif
189 wine_tsx11_lock();
190 if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
191 X11DRV_XRender_Installed = TRUE;
192 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
193 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
194 if(!pict_formats[color_drawable])
196 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
197 if (visual->class == DirectColor)
199 XVisualInfo info;
200 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
201 screen_depth, TrueColor, &info ))
203 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
204 if (pict_formats[color_drawable]) visual = info.visual;
208 if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
210 wine_tsx11_unlock();
211 WINE_MESSAGE(
212 "Wine has detected that you probably have a buggy version\n"
213 "of libXrender.so . Because of this client side font rendering\n"
214 "will be disabled. Please upgrade this library.\n");
215 X11DRV_XRender_Installed = FALSE;
216 return;
218 pf.type = PictTypeDirect;
219 pf.depth = 1;
220 pf.direct.alpha = 0;
221 pf.direct.alphaMask = 1;
222 pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
223 PictFormatDepth | PictFormatAlpha |
224 PictFormatAlphaMask, &pf, 0);
225 if(!pict_formats[mono_drawable]) {
226 ERR("mono_format == NULL?\n");
227 X11DRV_XRender_Installed = FALSE;
229 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
230 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
231 X11DRV_XRender_Installed = FALSE;
234 wine_tsx11_unlock();
237 sym_not_found:
238 if(X11DRV_XRender_Installed || client_side_with_core)
240 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
241 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
243 glyphsetCacheSize = INIT_CACHE_SIZE;
244 lastfree = 0;
245 for(i = 0; i < INIT_CACHE_SIZE; i++) {
246 glyphsetCache[i].next = i + 1;
247 glyphsetCache[i].count = -1;
249 glyphsetCache[i-1].next = -1;
250 using_client_side_fonts = 1;
252 if(!X11DRV_XRender_Installed) {
253 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
254 if(screen_depth <= 8 || !client_side_antialias_with_core)
255 antialias = 0;
256 } else {
257 if(screen_depth <= 8 || !client_side_antialias_with_render)
258 antialias = 0;
261 else TRACE("Using X11 core fonts\n");
264 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
266 if(p1->hash != p2->hash) return TRUE;
267 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
268 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
269 return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
272 #if 0
273 static void walk_cache(void)
275 int i;
277 EnterCriticalSection(&xrender_cs);
278 for(i=mru; i >= 0; i = glyphsetCache[i].next)
279 TRACE("item %d\n", i);
280 LeaveCriticalSection(&xrender_cs);
282 #endif
284 static int LookupEntry(LFANDSIZE *plfsz)
286 int i, prev_i = -1;
288 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
289 TRACE("%d\n", i);
290 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
291 i = -1;
292 break;
295 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
296 glyphsetCache[i].count++;
297 if(prev_i >= 0) {
298 glyphsetCache[prev_i].next = glyphsetCache[i].next;
299 glyphsetCache[i].next = mru;
300 mru = i;
302 TRACE("found font in cache %d\n", i);
303 return i;
305 prev_i = i;
307 TRACE("font not in cache\n");
308 return -1;
311 static void FreeEntry(int entry)
313 int i, format;
315 for(format = 0; format < AA_MAXVALUE; format++) {
316 gsCacheEntryFormat * formatEntry;
318 if( !glyphsetCache[entry].format[format] )
319 continue;
321 formatEntry = glyphsetCache[entry].format[format];
323 if(formatEntry->glyphset) {
324 wine_tsx11_lock();
325 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
326 wine_tsx11_unlock();
327 formatEntry->glyphset = 0;
329 if(formatEntry->nrealized) {
330 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
331 formatEntry->realized = NULL;
332 if(formatEntry->bitmaps) {
333 for(i = 0; i < formatEntry->nrealized; i++)
334 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
335 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
336 formatEntry->bitmaps = NULL;
338 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
339 formatEntry->gis = NULL;
340 formatEntry->nrealized = 0;
343 HeapFree(GetProcessHeap(), 0, formatEntry);
344 glyphsetCache[entry].format[format] = NULL;
348 static int AllocEntry(void)
350 int best = -1, prev_best = -1, i, prev_i = -1;
352 if(lastfree >= 0) {
353 assert(glyphsetCache[lastfree].count == -1);
354 glyphsetCache[lastfree].count = 1;
355 best = lastfree;
356 lastfree = glyphsetCache[lastfree].next;
357 assert(best != mru);
358 glyphsetCache[best].next = mru;
359 mru = best;
361 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
362 return mru;
365 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
366 if(glyphsetCache[i].count == 0) {
367 best = i;
368 prev_best = prev_i;
370 prev_i = i;
373 if(best >= 0) {
374 TRACE("freeing unused glyphset at cache %d\n", best);
375 FreeEntry(best);
376 glyphsetCache[best].count = 1;
377 if(prev_best >= 0) {
378 glyphsetCache[prev_best].next = glyphsetCache[best].next;
379 glyphsetCache[best].next = mru;
380 mru = best;
381 } else {
382 assert(mru == best);
384 return mru;
387 TRACE("Growing cache\n");
389 if (glyphsetCache)
390 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 glyphsetCache,
392 (glyphsetCacheSize + INIT_CACHE_SIZE)
393 * sizeof(*glyphsetCache));
394 else
395 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
396 (glyphsetCacheSize + INIT_CACHE_SIZE)
397 * sizeof(*glyphsetCache));
399 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
400 i++) {
401 glyphsetCache[i].next = i + 1;
402 glyphsetCache[i].count = -1;
404 glyphsetCache[i-1].next = -1;
405 glyphsetCacheSize += INIT_CACHE_SIZE;
407 lastfree = glyphsetCache[best].next;
408 glyphsetCache[best].count = 1;
409 glyphsetCache[best].next = mru;
410 mru = best;
411 TRACE("new free cache slot at %d\n", mru);
412 return mru;
415 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
417 DWORD size;
418 WORD *gasp, *buffer;
419 WORD num_recs;
420 DWORD ppem;
421 TEXTMETRICW tm;
423 *flags = 0;
425 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
426 if(size == GDI_ERROR)
427 return FALSE;
429 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
430 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
432 GetTextMetricsW(physDev->hdc, &tm);
433 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
435 gasp++;
436 num_recs = get_be_word(*gasp);
437 gasp++;
438 while(num_recs--)
440 *flags = get_be_word(*(gasp + 1));
441 if(ppem <= get_be_word(*gasp))
442 break;
443 gasp += 2;
445 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
447 HeapFree(GetProcessHeap(), 0, buffer);
448 return TRUE;
451 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
453 int ret;
454 int format;
455 gsCacheEntry *entry;
456 WORD flags;
457 static int hinter = -1;
459 if((ret = LookupEntry(plfsz)) != -1) return ret;
461 ret = AllocEntry();
462 entry = glyphsetCache + ret;
463 entry->lfsz = *plfsz;
464 for( format = 0; format < AA_MAXVALUE; format++ ) {
465 assert( !entry->format[format] );
468 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
470 if(hinter == -1)
472 RASTERIZER_STATUS status;
473 GetRasterizerCaps(&status, sizeof(status));
474 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
476 if(!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
477 entry->aa_default = AA_Grey;
478 else
479 entry->aa_default = AA_None;
481 else
482 entry->aa_default = AA_None;
484 return ret;
487 static void dec_ref_cache(int index)
489 assert(index >= 0);
490 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
491 assert(glyphsetCache[index].count > 0);
492 glyphsetCache[index].count--;
495 static void lfsz_calc_hash(LFANDSIZE *plfsz)
497 DWORD hash = 0, *ptr;
498 int i;
500 hash ^= plfsz->devsize.cx;
501 hash ^= plfsz->devsize.cy;
502 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
503 hash ^= *ptr;
504 for(i = 0, ptr = (DWORD*)&plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
505 WCHAR *pwc = (WCHAR *)ptr;
506 if(!*pwc) break;
507 hash ^= *ptr;
508 pwc++;
509 if(!*pwc) break;
511 plfsz->hash = hash;
512 return;
515 /***********************************************************************
516 * X11DRV_XRender_Finalize
518 void X11DRV_XRender_Finalize(void)
520 int i;
522 EnterCriticalSection(&xrender_cs);
523 for(i = mru; i >= 0; i = glyphsetCache[i].next)
524 FreeEntry(i);
525 LeaveCriticalSection(&xrender_cs);
529 /***********************************************************************
530 * X11DRV_XRender_SelectFont
532 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
534 LFANDSIZE lfsz;
536 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
537 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
538 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
539 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
540 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
541 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
542 lfsz_calc_hash(&lfsz);
544 EnterCriticalSection(&xrender_cs);
545 if(!physDev->xrender) {
546 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
547 sizeof(*physDev->xrender));
548 physDev->xrender->cache_index = -1;
550 else if(physDev->xrender->cache_index != -1)
551 dec_ref_cache(physDev->xrender->cache_index);
552 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
553 LeaveCriticalSection(&xrender_cs);
554 return 0;
557 /***********************************************************************
558 * X11DRV_XRender_DeleteDC
560 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
562 X11DRV_XRender_UpdateDrawable(physDev);
564 EnterCriticalSection(&xrender_cs);
565 if(physDev->xrender->cache_index != -1)
566 dec_ref_cache(physDev->xrender->cache_index);
567 LeaveCriticalSection(&xrender_cs);
569 HeapFree(GetProcessHeap(), 0, physDev->xrender);
570 physDev->xrender = NULL;
571 return;
574 /***********************************************************************
575 * X11DRV_XRender_UpdateDrawable
577 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
578 * It deletes the pict and tile when the drawable changes.
580 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
582 wine_tsx11_lock();
584 if(physDev->xrender->pict)
586 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
587 XFlush(gdi_display);
588 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
589 physDev->xrender->pict = 0;
591 wine_tsx11_unlock();
593 return;
596 /************************************************************************
597 * UploadGlyph
599 * Helper to ExtTextOut. Must be called inside xrender_cs
601 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
603 unsigned int buflen;
604 char *buf;
605 Glyph gid;
606 GLYPHMETRICS gm;
607 XGlyphInfo gi;
608 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
609 gsCacheEntryFormat *formatEntry;
610 UINT ggo_format = GGO_GLYPH_INDEX;
611 XRenderPictFormat pf;
612 static const char zero[4];
614 switch(format) {
615 case AA_Grey:
616 ggo_format |= WINE_GGO_GRAY16_BITMAP;
617 break;
619 default:
620 ERR("aa = %d - not implemented\n", format);
621 case AA_None:
622 ggo_format |= GGO_BITMAP;
623 break;
626 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
627 NULL);
628 if(buflen == GDI_ERROR) {
629 if(format != AA_None) {
630 format = AA_None;
631 entry->aa_default = AA_None;
632 ggo_format &= ~WINE_GGO_GRAY16_BITMAP;
633 ggo_format |= GGO_BITMAP;
634 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
635 NULL);
637 if(buflen == GDI_ERROR) {
638 WARN("GetGlyphOutlineW failed\n");
639 return FALSE;
641 TRACE("Turning off antialiasing for this monochrome font\n");
644 /* If there is nothing for the current type, we create the entry. */
645 if( !entry->format[format] ) {
646 entry->format[format] = HeapAlloc(GetProcessHeap(),
647 HEAP_ZERO_MEMORY,
648 sizeof(gsCacheEntryFormat));
650 formatEntry = entry->format[format];
652 if(formatEntry->nrealized <= glyph) {
653 formatEntry->nrealized = (glyph / 128 + 1) * 128;
655 if (formatEntry->realized)
656 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
657 HEAP_ZERO_MEMORY,
658 formatEntry->realized,
659 formatEntry->nrealized * sizeof(BOOL));
660 else
661 formatEntry->realized = HeapAlloc(GetProcessHeap(),
662 HEAP_ZERO_MEMORY,
663 formatEntry->nrealized * sizeof(BOOL));
665 if(!X11DRV_XRender_Installed) {
666 if (formatEntry->bitmaps)
667 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
668 HEAP_ZERO_MEMORY,
669 formatEntry->bitmaps,
670 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
671 else
672 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
673 HEAP_ZERO_MEMORY,
674 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
676 if (formatEntry->gis)
677 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
678 HEAP_ZERO_MEMORY,
679 formatEntry->gis,
680 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
681 else
682 formatEntry->gis = HeapAlloc(GetProcessHeap(),
683 HEAP_ZERO_MEMORY,
684 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
688 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
689 switch(format) {
690 case AA_Grey:
691 pf.depth = 8;
692 pf.direct.alphaMask = 0xff;
693 break;
695 default:
696 ERR("aa = %d - not implemented\n", format);
697 case AA_None:
698 pf.depth = 1;
699 pf.direct.alphaMask = 1;
700 break;
703 pf.type = PictTypeDirect;
704 pf.direct.alpha = 0;
706 wine_tsx11_lock();
707 formatEntry->font_format = pXRenderFindFormat(gdi_display,
708 PictFormatType |
709 PictFormatDepth |
710 PictFormatAlpha |
711 PictFormatAlphaMask,
712 &pf, 0);
714 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
715 wine_tsx11_unlock();
719 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
720 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, NULL);
721 formatEntry->realized[glyph] = TRUE;
723 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
724 buflen,
725 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
726 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
728 gi.width = gm.gmBlackBoxX;
729 gi.height = gm.gmBlackBoxY;
730 gi.x = -gm.gmptGlyphOrigin.x;
731 gi.y = gm.gmptGlyphOrigin.y;
732 gi.xOff = gm.gmCellIncX;
733 gi.yOff = gm.gmCellIncY;
735 if(TRACE_ON(xrender)) {
736 int pitch, i, j;
737 char output[300];
738 unsigned char *line;
740 if(format == AA_None) {
741 pitch = ((gi.width + 31) / 32) * 4;
742 for(i = 0; i < gi.height; i++) {
743 line = (unsigned char*) buf + i * pitch;
744 output[0] = '\0';
745 for(j = 0; j < pitch * 8; j++) {
746 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
748 strcat(output, "\n");
749 TRACE(output);
751 } else {
752 static const char blks[] = " .:;!o*#";
753 char str[2];
755 str[1] = '\0';
756 pitch = ((gi.width + 3) / 4) * 4;
757 for(i = 0; i < gi.height; i++) {
758 line = (unsigned char*) buf + i * pitch;
759 output[0] = '\0';
760 for(j = 0; j < pitch; j++) {
761 str[0] = blks[line[j] >> 5];
762 strcat(output, str);
764 strcat(output, "\n");
765 TRACE(output);
771 if(formatEntry->glyphset) {
772 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
773 unsigned char *byte = (unsigned char*) buf, c;
774 int i = buflen;
776 while(i--) {
777 c = *byte;
779 /* magic to flip bit order */
780 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
781 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
782 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
784 *byte++ = c;
787 gid = glyph;
790 XRenderCompositeText seems to ignore 0x0 glyphs when
791 AA_None, which means we lose the advance width of glyphs
792 like the space. We'll pretend that such glyphs are 1x1
793 bitmaps.
796 if(buflen == 0)
797 gi.width = gi.height = 1;
799 wine_tsx11_lock();
800 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
801 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
802 wine_tsx11_unlock();
803 HeapFree(GetProcessHeap(), 0, buf);
804 } else {
805 formatEntry->bitmaps[glyph] = buf;
808 memcpy(&formatEntry->gis[glyph], &gi, sizeof(gi));
810 return TRUE;
813 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
814 void *bitmap, XGlyphInfo *gi)
816 unsigned char *srcLine = bitmap, *src;
817 unsigned char bits, bitsMask;
818 int width = gi->width;
819 int stride = ((width + 31) & ~31) >> 3;
820 int height = gi->height;
821 int w;
822 int xspan, lenspan;
824 TRACE("%d, %d\n", x, y);
825 x -= gi->x;
826 y -= gi->y;
827 while (height--)
829 src = srcLine;
830 srcLine += stride;
831 w = width;
833 bitsMask = 0x80; /* FreeType is always MSB first */
834 bits = *src++;
836 xspan = x;
837 while (w)
839 if (bits & bitsMask)
841 lenspan = 0;
844 lenspan++;
845 if (lenspan == w)
846 break;
847 bitsMask = bitsMask >> 1;
848 if (!bitsMask)
850 bits = *src++;
851 bitsMask = 0x80;
853 } while (bits & bitsMask);
854 XFillRectangle (gdi_display, physDev->drawable,
855 physDev->gc, xspan, y, lenspan, 1);
856 xspan += lenspan;
857 w -= lenspan;
859 else
863 w--;
864 xspan++;
865 if (!w)
866 break;
867 bitsMask = bitsMask >> 1;
868 if (!bitsMask)
870 bits = *src++;
871 bitsMask = 0x80;
873 } while (!(bits & bitsMask));
876 y++;
880 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
881 void *bitmap, XGlyphInfo *gi)
883 unsigned char *srcLine = bitmap, *src, bits;
884 int width = gi->width;
885 int stride = ((width + 3) & ~3);
886 int height = gi->height;
887 int w;
888 int xspan, lenspan;
890 x -= gi->x;
891 y -= gi->y;
892 while (height--)
894 src = srcLine;
895 srcLine += stride;
896 w = width;
898 bits = *src++;
899 xspan = x;
900 while (w)
902 if (bits >= 0x80)
904 lenspan = 0;
907 lenspan++;
908 if (lenspan == w)
909 break;
910 bits = *src++;
911 } while (bits >= 0x80);
912 XFillRectangle (gdi_display, physDev->drawable,
913 physDev->gc, xspan, y, lenspan, 1);
914 xspan += lenspan;
915 w -= lenspan;
917 else
921 w--;
922 xspan++;
923 if (!w)
924 break;
925 bits = *src++;
926 } while (bits < 0x80);
929 y++;
934 static void ExamineBitfield (DWORD mask, int *shift, int *len)
936 int s, l;
938 s = 0;
939 while ((mask & 1) == 0)
941 mask >>= 1;
942 s++;
944 l = 0;
945 while ((mask & 1) == 1)
947 mask >>= 1;
948 l++;
950 *shift = s;
951 *len = l;
954 static DWORD GetField (DWORD pixel, int shift, int len)
956 pixel = pixel & (((1 << (len)) - 1) << shift);
957 pixel = pixel << (32 - (shift + len)) >> 24;
958 while (len < 8)
960 pixel |= (pixel >> len);
961 len <<= 1;
963 return pixel;
967 static DWORD PutField (DWORD pixel, int shift, int len)
969 shift = shift - (8 - len);
970 if (len <= 8)
971 pixel &= (((1 << len) - 1) << (8 - len));
972 if (shift < 0)
973 pixel >>= -shift;
974 else
975 pixel <<= shift;
976 return pixel;
979 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
980 int color)
982 int r_shift, r_len;
983 int g_shift, g_len;
984 int b_shift, b_len;
985 BYTE *maskLine, *mask, m;
986 int maskStride;
987 DWORD pixel;
988 int width, height;
989 int w, tx;
990 BYTE src_r, src_g, src_b;
992 x -= gi->x;
993 y -= gi->y;
994 width = gi->width;
995 height = gi->height;
997 maskLine = (unsigned char *) bitmap;
998 maskStride = (width + 3) & ~3;
1000 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1001 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1002 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1004 src_r = GetField(color, r_shift, r_len);
1005 src_g = GetField(color, g_shift, g_len);
1006 src_b = GetField(color, b_shift, b_len);
1008 for(; height--; y++)
1010 mask = maskLine;
1011 maskLine += maskStride;
1012 w = width;
1013 tx = x;
1015 if(y < 0) continue;
1016 if(y >= image->height) break;
1018 for(; w--; tx++)
1020 if(tx >= image->width) break;
1022 m = *mask++;
1023 if(tx < 0) continue;
1025 if (m == 0xff)
1026 XPutPixel (image, tx, y, color);
1027 else if (m)
1029 BYTE r, g, b;
1031 pixel = XGetPixel (image, tx, y);
1033 r = GetField(pixel, r_shift, r_len);
1034 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1035 g = GetField(pixel, g_shift, g_len);
1036 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1037 b = GetField(pixel, b_shift, b_len);
1038 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1040 pixel = (PutField (r, r_shift, r_len) |
1041 PutField (g, g_shift, g_len) |
1042 PutField (b, b_shift, b_len));
1043 XPutPixel (image, tx, y, pixel);
1049 /*************************************************************
1050 * get_tile_pict
1052 * Returns an appropiate Picture for tiling the text colour.
1053 * Call and use result within the xrender_cs
1055 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1057 static struct
1059 Pixmap xpm;
1060 Picture pict;
1061 int current_color;
1062 } tiles[2], *tile;
1063 XRenderColor col;
1065 tile = &tiles[type];
1067 if(!tile->xpm)
1069 XRenderPictureAttributes pa;
1071 wine_tsx11_lock();
1072 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1074 pa.repeat = True;
1075 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1076 wine_tsx11_unlock();
1078 /* init current_color to something different from text_pixel */
1079 tile->current_color = ~text_pixel;
1081 if(type == mono_drawable)
1083 /* for a 1bpp bitmap we always need a 1 in the tile */
1084 col.red = col.green = col.blue = 0;
1085 col.alpha = 0xffff;
1086 wine_tsx11_lock();
1087 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1088 wine_tsx11_unlock();
1092 if(text_pixel != tile->current_color && type == color_drawable)
1094 /* Map 0 -- 0xff onto 0 -- 0xffff */
1095 int r_shift, r_len;
1096 int g_shift, g_len;
1097 int b_shift, b_len;
1099 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1100 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1101 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1103 col.red = GetField(text_pixel, r_shift, r_len);
1104 col.red |= col.red << 8;
1105 col.green = GetField(text_pixel, g_shift, g_len);
1106 col.green |= col.green << 8;
1107 col.blue = GetField(text_pixel, b_shift, b_len);
1108 col.blue |= col.blue << 8;
1109 col.alpha = 0x0;
1111 wine_tsx11_lock();
1112 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1113 wine_tsx11_unlock();
1114 tile->current_color = text_pixel;
1116 return tile->pict;
1119 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1121 return 1;
1124 /***********************************************************************
1125 * X11DRV_XRender_ExtTextOut
1127 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1128 const RECT *lprect, LPCWSTR wstr, UINT count,
1129 const INT *lpDx )
1131 RGNDATA *data;
1132 XGCValues xgcval;
1133 int render_op = PictOpOver;
1134 gsCacheEntry *entry;
1135 gsCacheEntryFormat *formatEntry;
1136 BOOL retv = FALSE;
1137 HDC hdc = physDev->hdc;
1138 int textPixel, backgroundPixel;
1139 HRGN saved_region = 0;
1140 BOOL disable_antialias = FALSE;
1141 AA_Type aa_type = AA_None;
1142 DIBSECTION bmp;
1143 unsigned int idx;
1144 double cosEsc, sinEsc;
1145 LOGFONTW lf;
1146 enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1147 Picture tile_pict = 0;
1149 /* Do we need to disable antialiasing because of palette mode? */
1150 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1151 TRACE("bitmap is not a DIB\n");
1153 else if (bmp.dsBmih.biBitCount <= 8) {
1154 TRACE("Disabling antialiasing\n");
1155 disable_antialias = TRUE;
1158 xgcval.function = GXcopy;
1159 xgcval.background = physDev->backgroundPixel;
1160 xgcval.fill_style = FillSolid;
1161 wine_tsx11_lock();
1162 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1163 wine_tsx11_unlock();
1165 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod, FALSE );
1167 if(physDev->depth == 1) {
1168 if((physDev->textPixel & 0xffffff) == 0) {
1169 textPixel = 0;
1170 backgroundPixel = 1;
1171 } else {
1172 textPixel = 1;
1173 backgroundPixel = 0;
1175 } else {
1176 textPixel = physDev->textPixel;
1177 backgroundPixel = physDev->backgroundPixel;
1180 if(flags & ETO_OPAQUE)
1182 wine_tsx11_lock();
1183 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1184 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1185 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1186 lprect->right - lprect->left, lprect->bottom - lprect->top );
1187 wine_tsx11_unlock();
1190 if(count == 0)
1192 retv = TRUE;
1193 goto done_unlock;
1197 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1198 if(lf.lfEscapement != 0) {
1199 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1200 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1201 } else {
1202 cosEsc = 1;
1203 sinEsc = 0;
1206 if (flags & ETO_CLIPPED)
1208 HRGN clip_region;
1210 clip_region = CreateRectRgnIndirect( lprect );
1211 /* make a copy of the current device region */
1212 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1213 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1214 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1215 DeleteObject( clip_region );
1218 EnterCriticalSection(&xrender_cs);
1220 if(X11DRV_XRender_Installed) {
1221 if(!physDev->xrender->pict) {
1222 XRenderPictureAttributes pa;
1223 pa.subwindow_mode = IncludeInferiors;
1225 wine_tsx11_lock();
1226 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1227 physDev->drawable,
1228 pict_formats[depth_type],
1229 CPSubwindowMode, &pa);
1230 wine_tsx11_unlock();
1232 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1233 physDev->xrender->pict, hdc, physDev->drawable);
1234 } else {
1235 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1236 physDev->xrender->pict, hdc, physDev->drawable);
1239 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1241 wine_tsx11_lock();
1242 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1243 physDev->dc_rect.left, physDev->dc_rect.top,
1244 (XRectangle *)data->Buffer, data->rdh.nCount );
1245 wine_tsx11_unlock();
1246 HeapFree( GetProcessHeap(), 0, data );
1249 tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1251 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1253 if((depth_type == mono_drawable) && (textPixel == 0))
1254 render_op = PictOpOutReverse; /* This gives us 'black' text */
1257 entry = glyphsetCache + physDev->xrender->cache_index;
1258 if( disable_antialias == FALSE )
1259 aa_type = entry->aa_default;
1260 formatEntry = entry->format[aa_type];
1262 for(idx = 0; idx < count; idx++) {
1263 if( !formatEntry ) {
1264 UploadGlyph(physDev, wstr[idx], aa_type);
1265 /* re-evaluate antialias since aa_default may have changed */
1266 if( disable_antialias == FALSE )
1267 aa_type = entry->aa_default;
1268 formatEntry = entry->format[aa_type];
1269 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1270 UploadGlyph(physDev, wstr[idx], aa_type);
1273 if (!formatEntry)
1275 WARN("could not upload requested glyphs\n");
1276 LeaveCriticalSection(&xrender_cs);
1277 goto done_unlock;
1280 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1281 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1283 if(X11DRV_XRender_Installed)
1285 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1286 INT offset = 0;
1287 POINT desired, current;
1289 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1290 So we pass zeros to the function and move to our starting position using the first
1291 element of the elts array. */
1293 desired.x = physDev->dc_rect.left + x;
1294 desired.y = physDev->dc_rect.top + y;
1295 current.x = current.y = 0;
1297 for(idx = 0; idx < count; idx++)
1299 elts[idx].glyphset = formatEntry->glyphset;
1300 elts[idx].chars = wstr + idx;
1301 elts[idx].nchars = 1;
1302 elts[idx].xOff = desired.x - current.x;
1303 elts[idx].yOff = desired.y - current.y;
1305 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1306 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1308 if(!lpDx)
1310 desired.x += formatEntry->gis[wstr[idx]].xOff;
1311 desired.y += formatEntry->gis[wstr[idx]].yOff;
1313 else
1315 offset += lpDx[idx];
1316 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1317 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1320 wine_tsx11_lock();
1321 pXRenderCompositeText16(gdi_display, render_op,
1322 tile_pict,
1323 physDev->xrender->pict,
1324 formatEntry->font_format,
1325 0, 0, 0, 0, elts, count);
1326 wine_tsx11_unlock();
1327 HeapFree(GetProcessHeap(), 0, elts);
1328 } else {
1329 INT offset = 0, xoff = 0, yoff = 0;
1330 wine_tsx11_lock();
1331 XSetForeground( gdi_display, physDev->gc, textPixel );
1333 if(aa_type == AA_None || physDev->depth == 1)
1335 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1337 if(aa_type == AA_None)
1338 sharp_glyph_fn = SharpGlyphMono;
1339 else
1340 sharp_glyph_fn = SharpGlyphGray;
1342 for(idx = 0; idx < count; idx++) {
1343 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1344 physDev->dc_rect.top + y + yoff,
1345 formatEntry->bitmaps[wstr[idx]],
1346 &formatEntry->gis[wstr[idx]]);
1347 if(lpDx) {
1348 offset += lpDx[idx];
1349 xoff = offset * cosEsc;
1350 yoff = offset * -sinEsc;
1351 } else {
1352 xoff += formatEntry->gis[wstr[idx]].xOff;
1353 yoff += formatEntry->gis[wstr[idx]].yOff;
1356 } else {
1357 XImage *image;
1358 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1359 RECT extents = {0, 0, 0, 0};
1360 POINT cur = {0, 0};
1361 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1362 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1364 TRACE("drawable %dx%d\n", w, h);
1366 for(idx = 0; idx < count; idx++) {
1367 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1368 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1369 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1370 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1371 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1372 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1373 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1374 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1375 if(lpDx) {
1376 offset += lpDx[idx];
1377 cur.x = offset * cosEsc;
1378 cur.y = offset * -sinEsc;
1379 } else {
1380 cur.x += formatEntry->gis[wstr[idx]].xOff;
1381 cur.y += formatEntry->gis[wstr[idx]].yOff;
1384 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1385 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1387 if(physDev->dc_rect.left + x + extents.left >= 0) {
1388 image_x = physDev->dc_rect.left + x + extents.left;
1389 image_off_x = 0;
1390 } else {
1391 image_x = 0;
1392 image_off_x = physDev->dc_rect.left + x + extents.left;
1394 if(physDev->dc_rect.top + y + extents.top >= 0) {
1395 image_y = physDev->dc_rect.top + y + extents.top;
1396 image_off_y = 0;
1397 } else {
1398 image_y = 0;
1399 image_off_y = physDev->dc_rect.top + y + extents.top;
1401 if(physDev->dc_rect.left + x + extents.right < w)
1402 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1403 else
1404 image_w = w - image_x;
1405 if(physDev->dc_rect.top + y + extents.bottom < h)
1406 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1407 else
1408 image_h = h - image_y;
1410 if(image_w <= 0 || image_h <= 0) goto no_image;
1412 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1413 image = XGetImage(gdi_display, physDev->drawable,
1414 image_x, image_y, image_w, image_h,
1415 AllPlanes, ZPixmap);
1416 X11DRV_check_error();
1418 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1419 gdi_display, (int)physDev->drawable, image_x, image_y,
1420 image_w, image_h, AllPlanes, ZPixmap,
1421 physDev->depth, image);
1422 if(!image) {
1423 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1424 physDev->depth);
1425 GC gc;
1426 XGCValues gcv;
1428 gcv.graphics_exposures = False;
1429 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1430 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1431 image_w, image_h, 0, 0);
1432 XFreeGC(gdi_display, gc);
1433 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1434 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1435 ZPixmap);
1436 X11DRV_check_error();
1437 XFreePixmap(gdi_display, xpm);
1439 if(!image) goto no_image;
1441 image->red_mask = visual->red_mask;
1442 image->green_mask = visual->green_mask;
1443 image->blue_mask = visual->blue_mask;
1445 offset = xoff = yoff = 0;
1446 for(idx = 0; idx < count; idx++) {
1447 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1448 yoff + image_off_y - extents.top,
1449 formatEntry->bitmaps[wstr[idx]],
1450 &formatEntry->gis[wstr[idx]],
1451 physDev->textPixel);
1452 if(lpDx) {
1453 offset += lpDx[idx];
1454 xoff = offset * cosEsc;
1455 yoff = offset * -sinEsc;
1456 } else {
1457 xoff += formatEntry->gis[wstr[idx]].xOff;
1458 yoff += formatEntry->gis[wstr[idx]].yOff;
1461 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1462 image_x, image_y, image_w, image_h);
1463 XDestroyImage(image);
1465 no_image:
1466 wine_tsx11_unlock();
1468 LeaveCriticalSection(&xrender_cs);
1470 if (flags & ETO_CLIPPED)
1472 /* restore the device region */
1473 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1474 DeleteObject( saved_region );
1477 retv = TRUE;
1479 done_unlock:
1480 X11DRV_UnlockDIBSection( physDev, TRUE );
1481 return retv;
1484 /******************************************************************************
1485 * AlphaBlend (x11drv.@)
1487 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1488 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1489 BLENDFUNCTION blendfn)
1491 XRenderPictureAttributes pa;
1492 XRenderPictFormat *src_format;
1493 XRenderPictFormat argb32_templ = {
1494 0, /* id */
1495 PictTypeDirect, /* type */
1496 32, /* depth */
1497 { /* direct */
1498 16, /* direct.red */
1499 0xff, /* direct.redMask */
1500 8, /* direct.green */
1501 0xff, /* direct.greenMask */
1502 0, /* direct.blue */
1503 0xff, /* direct.blueMask */
1504 24, /* direct.alpha */
1505 0xff, /* direct.alphaMask */
1507 0, /* colormap */
1509 unsigned long argb32_templ_mask =
1510 PictFormatType |
1511 PictFormatDepth |
1512 PictFormatRed |
1513 PictFormatRedMask |
1514 PictFormatGreen |
1515 PictFormatGreenMask |
1516 PictFormatBlue |
1517 PictFormatBlueMask |
1518 PictFormatAlpha |
1519 PictFormatAlphaMask;
1521 Picture dst_pict, src_pict;
1522 Pixmap xpm;
1523 DIBSECTION dib;
1524 XImage *image;
1525 GC gc;
1526 XGCValues gcv;
1527 BYTE *dstbits, *data;
1528 int y, y2;
1529 POINT pts[2];
1530 BOOL top_down = FALSE;
1531 RGNDATA *rgndata;
1532 enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1534 if(!X11DRV_XRender_Installed) {
1535 FIXME("Unable to AlphaBlend without Xrender\n");
1536 return FALSE;
1538 pts[0].x = xDst;
1539 pts[0].y = yDst;
1540 pts[1].x = xDst + widthDst;
1541 pts[1].y = yDst + heightDst;
1542 LPtoDP(devDst->hdc, pts, 2);
1543 xDst = pts[0].x;
1544 yDst = pts[0].y;
1545 widthDst = pts[1].x - pts[0].x;
1546 heightDst = pts[1].y - pts[0].y;
1548 pts[0].x = xSrc;
1549 pts[0].y = ySrc;
1550 pts[1].x = xSrc + widthSrc;
1551 pts[1].y = ySrc + heightSrc;
1552 LPtoDP(devSrc->hdc, pts, 2);
1553 xSrc = pts[0].x;
1554 ySrc = pts[0].y;
1555 widthSrc = pts[1].x - pts[0].x;
1556 heightSrc = pts[1].y - pts[0].y;
1557 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1559 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1560 if(widthDst != widthSrc || heightDst != heightSrc)
1561 #else
1562 if(!pXRenderSetPictureTransform)
1563 #endif
1565 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1566 return FALSE;
1569 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1571 FIXME("not a dibsection\n");
1572 return FALSE;
1575 if(dib.dsBm.bmBitsPixel != 32) {
1576 FIXME("not a 32 bpp dibsection\n");
1577 return FALSE;
1579 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1581 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1582 top_down = TRUE;
1583 dstbits += widthSrc * (heightSrc - 1) * 4;
1584 y2 = ySrc;
1585 y = y2 + heightSrc - 1;
1587 else
1589 y = dib.dsBmih.biHeight - ySrc - 1;
1590 y2 = y - heightSrc + 1;
1592 for(; y >= y2; y--) {
1593 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1594 widthSrc * 4);
1595 dstbits += (top_down ? -1 : 1) * widthSrc * 4;
1598 wine_tsx11_lock();
1599 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1600 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1603 Avoid using XRenderFindStandardFormat as older libraries don't have it
1604 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1606 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1608 TRACE("src_format %p\n", src_format);
1610 pa.subwindow_mode = IncludeInferiors;
1612 /* FIXME use devDst->xrender->pict ? */
1613 dst_pict = pXRenderCreatePicture(gdi_display,
1614 devDst->drawable,
1615 pict_formats[dst_depth_type],
1616 CPSubwindowMode, &pa);
1617 TRACE("dst_pict %08lx\n", dst_pict);
1618 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1619 xpm = XCreatePixmap(gdi_display,
1620 root_window,
1621 widthSrc, heightSrc, 32);
1622 gcv.graphics_exposures = False;
1623 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1624 TRACE("xpm = %08lx\n", xpm);
1625 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1627 src_pict = pXRenderCreatePicture(gdi_display,
1628 xpm, src_format,
1629 CPSubwindowMode, &pa);
1630 TRACE("src_pict %08lx\n", src_pict);
1632 if ((rgndata = X11DRV_GetRegionData( devDst->region, 0 )))
1634 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1635 devDst->dc_rect.left, devDst->dc_rect.top,
1636 (XRectangle *)rgndata->Buffer,
1637 rgndata->rdh.nCount );
1638 HeapFree( GetProcessHeap(), 0, rgndata );
1641 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1642 if(widthDst != widthSrc || heightDst != heightSrc) {
1643 double xscale = widthSrc/(double)widthDst;
1644 double yscale = heightSrc/(double)heightDst;
1645 XTransform xform = {{
1646 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1647 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1648 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1650 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1652 #endif
1653 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1654 0, 0, 0, 0,
1655 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1658 pXRenderFreePicture(gdi_display, src_pict);
1659 XFreePixmap(gdi_display, xpm);
1660 XFreeGC(gdi_display, gc);
1661 pXRenderFreePicture(gdi_display, dst_pict);
1662 image->data = NULL;
1663 XDestroyImage(image);
1665 wine_tsx11_unlock();
1666 HeapFree(GetProcessHeap(), 0, data);
1667 return TRUE;
1670 #else /* SONAME_LIBXRENDER */
1672 void X11DRV_XRender_Init(void)
1674 TRACE("XRender support not compiled in.\n");
1675 return;
1678 void X11DRV_XRender_Finalize(void)
1682 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1684 assert(0);
1685 return FALSE;
1688 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1690 assert(0);
1691 return;
1694 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1695 const RECT *lprect, LPCWSTR wstr, UINT count,
1696 const INT *lpDx )
1698 assert(0);
1699 return FALSE;
1702 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1704 assert(0);
1705 return;
1708 /******************************************************************************
1709 * AlphaBlend (x11drv.@)
1711 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1712 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1713 BLENDFUNCTION blendfn)
1715 FIXME("not supported - XRENDER headers were missing at compile time\n");
1716 return FALSE;
1719 #endif /* SONAME_LIBXRENDER */