push d1f5df181c120dbe494f7c89b454752c2e0dcc04
[wine/hacks.git] / dlls / winex11.drv / xrender.c
bloba7414a6033bce599b04aac47b6bb6484188f46c3
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 AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
456 AA_Type ret;
457 WORD flags;
458 UINT font_smoothing_type, font_smoothing_orientation;
460 if (X11DRV_XRender_Installed && subpixel &&
461 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
462 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
464 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
465 &font_smoothing_orientation, 0) &&
466 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
468 ret = AA_BGR;
470 else
471 ret = AA_RGB;
472 /*FIXME
473 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
474 But, Wine's subpixel rendering can support the portrait mode.
477 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
478 ret = AA_Grey;
479 else
480 ret = AA_None;
482 return ret;
485 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
487 int ret;
488 int format;
489 gsCacheEntry *entry;
490 static int hinter = -1;
491 static int subpixel = -1;
492 BOOL font_smoothing;
494 if((ret = LookupEntry(plfsz)) != -1) return ret;
496 ret = AllocEntry();
497 entry = glyphsetCache + ret;
498 entry->lfsz = *plfsz;
499 for( format = 0; format < AA_MAXVALUE; format++ ) {
500 assert( !entry->format[format] );
503 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
505 if(hinter == -1 || subpixel == -1)
507 RASTERIZER_STATUS status;
508 GetRasterizerCaps(&status, sizeof(status));
509 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
510 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
513 switch (plfsz->lf.lfQuality)
515 case ANTIALIASED_QUALITY:
516 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
517 break;
518 case CLEARTYPE_QUALITY:
519 case CLEARTYPE_NATURAL_QUALITY:
520 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
521 break;
522 case DEFAULT_QUALITY:
523 case DRAFT_QUALITY:
524 case PROOF_QUALITY:
525 default:
526 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
527 font_smoothing)
529 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
531 else
532 entry->aa_default = AA_None;
533 break;
536 else
537 entry->aa_default = AA_None;
539 return ret;
542 static void dec_ref_cache(int index)
544 assert(index >= 0);
545 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
546 assert(glyphsetCache[index].count > 0);
547 glyphsetCache[index].count--;
550 static void lfsz_calc_hash(LFANDSIZE *plfsz)
552 DWORD hash = 0, *ptr;
553 int i;
555 hash ^= plfsz->devsize.cx;
556 hash ^= plfsz->devsize.cy;
557 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
558 hash ^= *ptr;
559 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
560 WCHAR *pwc = (WCHAR *)ptr;
561 if(!*pwc) break;
562 hash ^= *ptr;
563 pwc++;
564 if(!*pwc) break;
566 plfsz->hash = hash;
567 return;
570 /***********************************************************************
571 * X11DRV_XRender_Finalize
573 void X11DRV_XRender_Finalize(void)
575 int i;
577 EnterCriticalSection(&xrender_cs);
578 for(i = mru; i >= 0; i = glyphsetCache[i].next)
579 FreeEntry(i);
580 LeaveCriticalSection(&xrender_cs);
584 /***********************************************************************
585 * X11DRV_XRender_SelectFont
587 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
589 LFANDSIZE lfsz;
591 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
592 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
593 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
594 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
595 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
596 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
597 lfsz_calc_hash(&lfsz);
599 EnterCriticalSection(&xrender_cs);
600 if(!physDev->xrender) {
601 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
602 sizeof(*physDev->xrender));
603 physDev->xrender->cache_index = -1;
605 else if(physDev->xrender->cache_index != -1)
606 dec_ref_cache(physDev->xrender->cache_index);
607 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
608 LeaveCriticalSection(&xrender_cs);
609 return 0;
612 /***********************************************************************
613 * X11DRV_XRender_DeleteDC
615 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
617 X11DRV_XRender_UpdateDrawable(physDev);
619 EnterCriticalSection(&xrender_cs);
620 if(physDev->xrender->cache_index != -1)
621 dec_ref_cache(physDev->xrender->cache_index);
622 LeaveCriticalSection(&xrender_cs);
624 HeapFree(GetProcessHeap(), 0, physDev->xrender);
625 physDev->xrender = NULL;
626 return;
629 /***********************************************************************
630 * X11DRV_XRender_UpdateDrawable
632 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
633 * It deletes the pict and tile when the drawable changes.
635 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
637 wine_tsx11_lock();
639 if(physDev->xrender->pict)
641 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
642 XFlush(gdi_display);
643 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
644 physDev->xrender->pict = 0;
646 wine_tsx11_unlock();
648 return;
651 /************************************************************************
652 * UploadGlyph
654 * Helper to ExtTextOut. Must be called inside xrender_cs
656 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
658 unsigned int buflen;
659 char *buf;
660 Glyph gid;
661 GLYPHMETRICS gm;
662 XGlyphInfo gi;
663 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
664 gsCacheEntryFormat *formatEntry;
665 UINT ggo_format = GGO_GLYPH_INDEX;
666 XRenderPictFormat pf;
667 unsigned long pf_mask;
668 static const char zero[4];
669 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
671 switch(format) {
672 case AA_Grey:
673 ggo_format |= WINE_GGO_GRAY16_BITMAP;
674 break;
675 case AA_RGB:
676 ggo_format |= WINE_GGO_HRGB_BITMAP;
677 break;
678 case AA_BGR:
679 ggo_format |= WINE_GGO_HBGR_BITMAP;
680 break;
681 case AA_VRGB:
682 ggo_format |= WINE_GGO_VRGB_BITMAP;
683 break;
684 case AA_VBGR:
685 ggo_format |= WINE_GGO_VBGR_BITMAP;
686 break;
688 default:
689 ERR("aa = %d - not implemented\n", format);
690 case AA_None:
691 ggo_format |= GGO_BITMAP;
692 break;
695 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
696 if(buflen == GDI_ERROR) {
697 if(format != AA_None) {
698 format = AA_None;
699 entry->aa_default = AA_None;
700 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
701 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
703 if(buflen == GDI_ERROR) {
704 WARN("GetGlyphOutlineW failed\n");
705 return FALSE;
707 TRACE("Turning off antialiasing for this monochrome font\n");
710 /* If there is nothing for the current type, we create the entry. */
711 if( !entry->format[format] ) {
712 entry->format[format] = HeapAlloc(GetProcessHeap(),
713 HEAP_ZERO_MEMORY,
714 sizeof(gsCacheEntryFormat));
716 formatEntry = entry->format[format];
718 if(formatEntry->nrealized <= glyph) {
719 formatEntry->nrealized = (glyph / 128 + 1) * 128;
721 if (formatEntry->realized)
722 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
723 HEAP_ZERO_MEMORY,
724 formatEntry->realized,
725 formatEntry->nrealized * sizeof(BOOL));
726 else
727 formatEntry->realized = HeapAlloc(GetProcessHeap(),
728 HEAP_ZERO_MEMORY,
729 formatEntry->nrealized * sizeof(BOOL));
731 if(!X11DRV_XRender_Installed) {
732 if (formatEntry->bitmaps)
733 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
734 HEAP_ZERO_MEMORY,
735 formatEntry->bitmaps,
736 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
737 else
738 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
739 HEAP_ZERO_MEMORY,
740 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
742 if (formatEntry->gis)
743 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
744 HEAP_ZERO_MEMORY,
745 formatEntry->gis,
746 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
747 else
748 formatEntry->gis = HeapAlloc(GetProcessHeap(),
749 HEAP_ZERO_MEMORY,
750 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
754 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
755 switch(format) {
756 case AA_Grey:
757 pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
758 pf.type = PictTypeDirect;
759 pf.depth = 8;
760 pf.direct.alpha = 0;
761 pf.direct.alphaMask = 0xff;
762 break;
764 case AA_RGB:
765 case AA_BGR:
766 case AA_VRGB:
767 case AA_VBGR:
768 pf_mask = PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask |
769 PictFormatGreen | PictFormatGreenMask | PictFormatBlue |
770 PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask;
771 pf.type = PictTypeDirect;
772 pf.depth = 32;
773 pf.direct.red = 16;
774 pf.direct.redMask = 0xff;
775 pf.direct.green = 8;
776 pf.direct.greenMask = 0xff;
777 pf.direct.blue = 0;
778 pf.direct.blueMask = 0xff;
779 pf.direct.alpha = 24;
780 pf.direct.alphaMask = 0xff;
781 break;
783 default:
784 ERR("aa = %d - not implemented\n", format);
785 case AA_None:
786 pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
787 pf.type = PictTypeDirect;
788 pf.depth = 1;
789 pf.direct.alpha = 0;
790 pf.direct.alphaMask = 1;
791 break;
794 wine_tsx11_lock();
795 formatEntry->font_format = pXRenderFindFormat(gdi_display, pf_mask, &pf, 0);
796 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
797 wine_tsx11_unlock();
801 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
802 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
803 formatEntry->realized[glyph] = TRUE;
805 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
806 buflen,
807 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
808 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
810 gi.width = gm.gmBlackBoxX;
811 gi.height = gm.gmBlackBoxY;
812 gi.x = -gm.gmptGlyphOrigin.x;
813 gi.y = gm.gmptGlyphOrigin.y;
814 gi.xOff = gm.gmCellIncX;
815 gi.yOff = gm.gmCellIncY;
817 if(TRACE_ON(xrender)) {
818 int pitch, i, j;
819 char output[300];
820 unsigned char *line;
822 if(format == AA_None) {
823 pitch = ((gi.width + 31) / 32) * 4;
824 for(i = 0; i < gi.height; i++) {
825 line = (unsigned char*) buf + i * pitch;
826 output[0] = '\0';
827 for(j = 0; j < pitch * 8; j++) {
828 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
830 TRACE("%s\n", output);
832 } else {
833 static const char blks[] = " .:;!o*#";
834 char str[2];
836 str[1] = '\0';
837 pitch = ((gi.width + 3) / 4) * 4;
838 for(i = 0; i < gi.height; i++) {
839 line = (unsigned char*) buf + i * pitch;
840 output[0] = '\0';
841 for(j = 0; j < pitch; j++) {
842 str[0] = blks[line[j] >> 5];
843 strcat(output, str);
845 TRACE("%s\n", output);
851 if(formatEntry->glyphset) {
852 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
853 unsigned char *byte = (unsigned char*) buf, c;
854 int i = buflen;
856 while(i--) {
857 c = *byte;
859 /* magic to flip bit order */
860 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
861 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
862 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
864 *byte++ = c;
867 else if ( format != AA_Grey &&
868 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
870 unsigned int i, *data = (unsigned int *)buf;
871 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
873 gid = glyph;
876 XRenderCompositeText seems to ignore 0x0 glyphs when
877 AA_None, which means we lose the advance width of glyphs
878 like the space. We'll pretend that such glyphs are 1x1
879 bitmaps.
882 if(buflen == 0)
883 gi.width = gi.height = 1;
885 wine_tsx11_lock();
886 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
887 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
888 wine_tsx11_unlock();
889 HeapFree(GetProcessHeap(), 0, buf);
890 } else {
891 formatEntry->bitmaps[glyph] = buf;
894 formatEntry->gis[glyph] = gi;
896 return TRUE;
899 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
900 void *bitmap, XGlyphInfo *gi)
902 unsigned char *srcLine = bitmap, *src;
903 unsigned char bits, bitsMask;
904 int width = gi->width;
905 int stride = ((width + 31) & ~31) >> 3;
906 int height = gi->height;
907 int w;
908 int xspan, lenspan;
910 TRACE("%d, %d\n", x, y);
911 x -= gi->x;
912 y -= gi->y;
913 while (height--)
915 src = srcLine;
916 srcLine += stride;
917 w = width;
919 bitsMask = 0x80; /* FreeType is always MSB first */
920 bits = *src++;
922 xspan = x;
923 while (w)
925 if (bits & bitsMask)
927 lenspan = 0;
930 lenspan++;
931 if (lenspan == w)
932 break;
933 bitsMask = bitsMask >> 1;
934 if (!bitsMask)
936 bits = *src++;
937 bitsMask = 0x80;
939 } while (bits & bitsMask);
940 XFillRectangle (gdi_display, physDev->drawable,
941 physDev->gc, xspan, y, lenspan, 1);
942 xspan += lenspan;
943 w -= lenspan;
945 else
949 w--;
950 xspan++;
951 if (!w)
952 break;
953 bitsMask = bitsMask >> 1;
954 if (!bitsMask)
956 bits = *src++;
957 bitsMask = 0x80;
959 } while (!(bits & bitsMask));
962 y++;
966 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
967 void *bitmap, XGlyphInfo *gi)
969 unsigned char *srcLine = bitmap, *src, bits;
970 int width = gi->width;
971 int stride = ((width + 3) & ~3);
972 int height = gi->height;
973 int w;
974 int xspan, lenspan;
976 x -= gi->x;
977 y -= gi->y;
978 while (height--)
980 src = srcLine;
981 srcLine += stride;
982 w = width;
984 bits = *src++;
985 xspan = x;
986 while (w)
988 if (bits >= 0x80)
990 lenspan = 0;
993 lenspan++;
994 if (lenspan == w)
995 break;
996 bits = *src++;
997 } while (bits >= 0x80);
998 XFillRectangle (gdi_display, physDev->drawable,
999 physDev->gc, xspan, y, lenspan, 1);
1000 xspan += lenspan;
1001 w -= lenspan;
1003 else
1007 w--;
1008 xspan++;
1009 if (!w)
1010 break;
1011 bits = *src++;
1012 } while (bits < 0x80);
1015 y++;
1020 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1022 int s, l;
1024 s = 0;
1025 while ((mask & 1) == 0)
1027 mask >>= 1;
1028 s++;
1030 l = 0;
1031 while ((mask & 1) == 1)
1033 mask >>= 1;
1034 l++;
1036 *shift = s;
1037 *len = l;
1040 static DWORD GetField (DWORD pixel, int shift, int len)
1042 pixel = pixel & (((1 << (len)) - 1) << shift);
1043 pixel = pixel << (32 - (shift + len)) >> 24;
1044 while (len < 8)
1046 pixel |= (pixel >> len);
1047 len <<= 1;
1049 return pixel;
1053 static DWORD PutField (DWORD pixel, int shift, int len)
1055 shift = shift - (8 - len);
1056 if (len <= 8)
1057 pixel &= (((1 << len) - 1) << (8 - len));
1058 if (shift < 0)
1059 pixel >>= -shift;
1060 else
1061 pixel <<= shift;
1062 return pixel;
1065 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1066 int color)
1068 int r_shift, r_len;
1069 int g_shift, g_len;
1070 int b_shift, b_len;
1071 BYTE *maskLine, *mask, m;
1072 int maskStride;
1073 DWORD pixel;
1074 int width, height;
1075 int w, tx;
1076 BYTE src_r, src_g, src_b;
1078 x -= gi->x;
1079 y -= gi->y;
1080 width = gi->width;
1081 height = gi->height;
1083 maskLine = bitmap;
1084 maskStride = (width + 3) & ~3;
1086 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1087 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1088 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1090 src_r = GetField(color, r_shift, r_len);
1091 src_g = GetField(color, g_shift, g_len);
1092 src_b = GetField(color, b_shift, b_len);
1094 for(; height--; y++)
1096 mask = maskLine;
1097 maskLine += maskStride;
1098 w = width;
1099 tx = x;
1101 if(y < 0) continue;
1102 if(y >= image->height) break;
1104 for(; w--; tx++)
1106 if(tx >= image->width) break;
1108 m = *mask++;
1109 if(tx < 0) continue;
1111 if (m == 0xff)
1112 XPutPixel (image, tx, y, color);
1113 else if (m)
1115 BYTE r, g, b;
1117 pixel = XGetPixel (image, tx, y);
1119 r = GetField(pixel, r_shift, r_len);
1120 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1121 g = GetField(pixel, g_shift, g_len);
1122 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1123 b = GetField(pixel, b_shift, b_len);
1124 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1126 pixel = (PutField (r, r_shift, r_len) |
1127 PutField (g, g_shift, g_len) |
1128 PutField (b, b_shift, b_len));
1129 XPutPixel (image, tx, y, pixel);
1135 /*************************************************************
1136 * get_tile_pict
1138 * Returns an appropriate Picture for tiling the text colour.
1139 * Call and use result within the xrender_cs
1141 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1143 static struct
1145 Pixmap xpm;
1146 Picture pict;
1147 int current_color;
1148 } tiles[2], *tile;
1149 XRenderColor col;
1151 tile = &tiles[type];
1153 if(!tile->xpm)
1155 XRenderPictureAttributes pa;
1157 wine_tsx11_lock();
1158 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1160 pa.repeat = True;
1161 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1162 wine_tsx11_unlock();
1164 /* init current_color to something different from text_pixel */
1165 tile->current_color = ~text_pixel;
1167 if(type == mono_drawable)
1169 /* for a 1bpp bitmap we always need a 1 in the tile */
1170 col.red = col.green = col.blue = 0;
1171 col.alpha = 0xffff;
1172 wine_tsx11_lock();
1173 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1174 wine_tsx11_unlock();
1178 if(text_pixel != tile->current_color && type == color_drawable)
1180 /* Map 0 -- 0xff onto 0 -- 0xffff */
1181 int r_shift, r_len;
1182 int g_shift, g_len;
1183 int b_shift, b_len;
1185 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1186 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1187 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1189 col.red = GetField(text_pixel, r_shift, r_len);
1190 col.red |= col.red << 8;
1191 col.green = GetField(text_pixel, g_shift, g_len);
1192 col.green |= col.green << 8;
1193 col.blue = GetField(text_pixel, b_shift, b_len);
1194 col.blue |= col.blue << 8;
1195 col.alpha = 0xffff;
1197 wine_tsx11_lock();
1198 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1199 wine_tsx11_unlock();
1200 tile->current_color = text_pixel;
1202 return tile->pict;
1205 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1207 return 1;
1210 /***********************************************************************
1211 * X11DRV_XRender_ExtTextOut
1213 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1214 const RECT *lprect, LPCWSTR wstr, UINT count,
1215 const INT *lpDx )
1217 RGNDATA *data;
1218 XGCValues xgcval;
1219 gsCacheEntry *entry;
1220 gsCacheEntryFormat *formatEntry;
1221 BOOL retv = FALSE;
1222 HDC hdc = physDev->hdc;
1223 int textPixel, backgroundPixel;
1224 HRGN saved_region = 0;
1225 BOOL disable_antialias = FALSE;
1226 AA_Type aa_type = AA_None;
1227 DIBSECTION bmp;
1228 unsigned int idx;
1229 double cosEsc, sinEsc;
1230 LOGFONTW lf;
1231 enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1232 Picture tile_pict = 0;
1234 /* Do we need to disable antialiasing because of palette mode? */
1235 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1236 TRACE("bitmap is not a DIB\n");
1238 else if (bmp.dsBmih.biBitCount <= 8) {
1239 TRACE("Disabling antialiasing\n");
1240 disable_antialias = TRUE;
1243 xgcval.function = GXcopy;
1244 xgcval.background = physDev->backgroundPixel;
1245 xgcval.fill_style = FillSolid;
1246 wine_tsx11_lock();
1247 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1248 wine_tsx11_unlock();
1250 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1252 if(physDev->depth == 1) {
1253 if((physDev->textPixel & 0xffffff) == 0) {
1254 textPixel = 0;
1255 backgroundPixel = 1;
1256 } else {
1257 textPixel = 1;
1258 backgroundPixel = 0;
1260 } else {
1261 textPixel = physDev->textPixel;
1262 backgroundPixel = physDev->backgroundPixel;
1265 if(flags & ETO_OPAQUE)
1267 wine_tsx11_lock();
1268 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1269 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1270 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1271 lprect->right - lprect->left, lprect->bottom - lprect->top );
1272 wine_tsx11_unlock();
1275 if(count == 0)
1277 retv = TRUE;
1278 goto done_unlock;
1282 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1283 if(lf.lfEscapement != 0) {
1284 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1285 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1286 } else {
1287 cosEsc = 1;
1288 sinEsc = 0;
1291 if (flags & ETO_CLIPPED)
1293 HRGN clip_region;
1295 clip_region = CreateRectRgnIndirect( lprect );
1296 /* make a copy of the current device region */
1297 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1298 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1299 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1300 DeleteObject( clip_region );
1303 if(X11DRV_XRender_Installed) {
1304 if(!physDev->xrender->pict) {
1305 XRenderPictureAttributes pa;
1306 pa.subwindow_mode = IncludeInferiors;
1308 wine_tsx11_lock();
1309 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1310 physDev->drawable,
1311 pict_formats[depth_type],
1312 CPSubwindowMode, &pa);
1313 wine_tsx11_unlock();
1315 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1316 physDev->xrender->pict, hdc, physDev->drawable);
1317 } else {
1318 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1319 physDev->xrender->pict, hdc, physDev->drawable);
1322 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1324 wine_tsx11_lock();
1325 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1326 physDev->dc_rect.left, physDev->dc_rect.top,
1327 (XRectangle *)data->Buffer, data->rdh.nCount );
1328 wine_tsx11_unlock();
1329 HeapFree( GetProcessHeap(), 0, data );
1333 EnterCriticalSection(&xrender_cs);
1335 entry = glyphsetCache + physDev->xrender->cache_index;
1336 if( disable_antialias == FALSE )
1337 aa_type = entry->aa_default;
1338 formatEntry = entry->format[aa_type];
1340 for(idx = 0; idx < count; idx++) {
1341 if( !formatEntry ) {
1342 UploadGlyph(physDev, wstr[idx], aa_type);
1343 /* re-evaluate antialias since aa_default may have changed */
1344 if( disable_antialias == FALSE )
1345 aa_type = entry->aa_default;
1346 formatEntry = entry->format[aa_type];
1347 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1348 UploadGlyph(physDev, wstr[idx], aa_type);
1351 if (!formatEntry)
1353 WARN("could not upload requested glyphs\n");
1354 LeaveCriticalSection(&xrender_cs);
1355 goto done_unlock;
1358 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1359 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1361 if(X11DRV_XRender_Installed)
1363 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1364 INT offset = 0;
1365 POINT desired, current;
1366 int render_op = PictOpOver;
1368 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1369 So we pass zeros to the function and move to our starting position using the first
1370 element of the elts array. */
1372 desired.x = physDev->dc_rect.left + x;
1373 desired.y = physDev->dc_rect.top + y;
1374 current.x = current.y = 0;
1376 tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1378 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1380 if((depth_type == mono_drawable) && (textPixel == 0))
1381 render_op = PictOpOutReverse; /* This gives us 'black' text */
1383 for(idx = 0; idx < count; idx++)
1385 elts[idx].glyphset = formatEntry->glyphset;
1386 elts[idx].chars = wstr + idx;
1387 elts[idx].nchars = 1;
1388 elts[idx].xOff = desired.x - current.x;
1389 elts[idx].yOff = desired.y - current.y;
1391 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1392 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1394 if(!lpDx)
1396 desired.x += formatEntry->gis[wstr[idx]].xOff;
1397 desired.y += formatEntry->gis[wstr[idx]].yOff;
1399 else
1401 offset += lpDx[idx];
1402 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1403 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1406 wine_tsx11_lock();
1407 pXRenderCompositeText16(gdi_display, render_op,
1408 tile_pict,
1409 physDev->xrender->pict,
1410 formatEntry->font_format,
1411 0, 0, 0, 0, elts, count);
1412 wine_tsx11_unlock();
1413 HeapFree(GetProcessHeap(), 0, elts);
1414 } else {
1415 INT offset = 0, xoff = 0, yoff = 0;
1416 wine_tsx11_lock();
1417 XSetForeground( gdi_display, physDev->gc, textPixel );
1419 if(aa_type == AA_None || physDev->depth == 1)
1421 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1423 if(aa_type == AA_None)
1424 sharp_glyph_fn = SharpGlyphMono;
1425 else
1426 sharp_glyph_fn = SharpGlyphGray;
1428 for(idx = 0; idx < count; idx++) {
1429 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1430 physDev->dc_rect.top + y + yoff,
1431 formatEntry->bitmaps[wstr[idx]],
1432 &formatEntry->gis[wstr[idx]]);
1433 if(lpDx) {
1434 offset += lpDx[idx];
1435 xoff = offset * cosEsc;
1436 yoff = offset * -sinEsc;
1437 } else {
1438 xoff += formatEntry->gis[wstr[idx]].xOff;
1439 yoff += formatEntry->gis[wstr[idx]].yOff;
1442 } else {
1443 XImage *image;
1444 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1445 RECT extents = {0, 0, 0, 0};
1446 POINT cur = {0, 0};
1447 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1448 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1450 TRACE("drawable %dx%d\n", w, h);
1452 for(idx = 0; idx < count; idx++) {
1453 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1454 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1455 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1456 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1457 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1458 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1459 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1460 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1461 if(lpDx) {
1462 offset += lpDx[idx];
1463 cur.x = offset * cosEsc;
1464 cur.y = offset * -sinEsc;
1465 } else {
1466 cur.x += formatEntry->gis[wstr[idx]].xOff;
1467 cur.y += formatEntry->gis[wstr[idx]].yOff;
1470 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1471 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1473 if(physDev->dc_rect.left + x + extents.left >= 0) {
1474 image_x = physDev->dc_rect.left + x + extents.left;
1475 image_off_x = 0;
1476 } else {
1477 image_x = 0;
1478 image_off_x = physDev->dc_rect.left + x + extents.left;
1480 if(physDev->dc_rect.top + y + extents.top >= 0) {
1481 image_y = physDev->dc_rect.top + y + extents.top;
1482 image_off_y = 0;
1483 } else {
1484 image_y = 0;
1485 image_off_y = physDev->dc_rect.top + y + extents.top;
1487 if(physDev->dc_rect.left + x + extents.right < w)
1488 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1489 else
1490 image_w = w - image_x;
1491 if(physDev->dc_rect.top + y + extents.bottom < h)
1492 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1493 else
1494 image_h = h - image_y;
1496 if(image_w <= 0 || image_h <= 0) goto no_image;
1498 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1499 image = XGetImage(gdi_display, physDev->drawable,
1500 image_x, image_y, image_w, image_h,
1501 AllPlanes, ZPixmap);
1502 X11DRV_check_error();
1504 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1505 gdi_display, (int)physDev->drawable, image_x, image_y,
1506 image_w, image_h, AllPlanes, ZPixmap,
1507 physDev->depth, image);
1508 if(!image) {
1509 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1510 physDev->depth);
1511 GC gc;
1512 XGCValues gcv;
1514 gcv.graphics_exposures = False;
1515 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1516 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1517 image_w, image_h, 0, 0);
1518 XFreeGC(gdi_display, gc);
1519 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1520 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1521 ZPixmap);
1522 X11DRV_check_error();
1523 XFreePixmap(gdi_display, xpm);
1525 if(!image) goto no_image;
1527 image->red_mask = visual->red_mask;
1528 image->green_mask = visual->green_mask;
1529 image->blue_mask = visual->blue_mask;
1531 offset = xoff = yoff = 0;
1532 for(idx = 0; idx < count; idx++) {
1533 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1534 yoff + image_off_y - extents.top,
1535 formatEntry->bitmaps[wstr[idx]],
1536 &formatEntry->gis[wstr[idx]],
1537 physDev->textPixel);
1538 if(lpDx) {
1539 offset += lpDx[idx];
1540 xoff = offset * cosEsc;
1541 yoff = offset * -sinEsc;
1542 } else {
1543 xoff += formatEntry->gis[wstr[idx]].xOff;
1544 yoff += formatEntry->gis[wstr[idx]].yOff;
1547 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1548 image_x, image_y, image_w, image_h);
1549 XDestroyImage(image);
1551 no_image:
1552 wine_tsx11_unlock();
1554 LeaveCriticalSection(&xrender_cs);
1556 if (flags & ETO_CLIPPED)
1558 /* restore the device region */
1559 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1560 DeleteObject( saved_region );
1563 retv = TRUE;
1565 done_unlock:
1566 X11DRV_UnlockDIBSection( physDev, TRUE );
1567 return retv;
1570 /******************************************************************************
1571 * AlphaBlend (x11drv.@)
1573 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1574 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1575 BLENDFUNCTION blendfn)
1577 XRenderPictureAttributes pa;
1578 XRenderPictFormat *src_format;
1579 XRenderPictFormat argb32_templ = {
1580 0, /* id */
1581 PictTypeDirect, /* type */
1582 32, /* depth */
1583 { /* direct */
1584 16, /* direct.red */
1585 0xff, /* direct.redMask */
1586 8, /* direct.green */
1587 0xff, /* direct.greenMask */
1588 0, /* direct.blue */
1589 0xff, /* direct.blueMask */
1590 24, /* direct.alpha */
1591 0xff, /* direct.alphaMask */
1593 0, /* colormap */
1595 unsigned long argb32_templ_mask =
1596 PictFormatType |
1597 PictFormatDepth |
1598 PictFormatRed |
1599 PictFormatRedMask |
1600 PictFormatGreen |
1601 PictFormatGreenMask |
1602 PictFormatBlue |
1603 PictFormatBlueMask |
1604 PictFormatAlpha |
1605 PictFormatAlphaMask;
1607 Picture dst_pict, src_pict;
1608 Pixmap xpm;
1609 DIBSECTION dib;
1610 XImage *image;
1611 GC gc;
1612 XGCValues gcv;
1613 DWORD *dstbits, *data;
1614 int y, y2;
1615 POINT pts[2];
1616 BOOL top_down = FALSE;
1617 RGNDATA *rgndata;
1618 enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1620 if(!X11DRV_XRender_Installed) {
1621 FIXME("Unable to AlphaBlend without Xrender\n");
1622 return FALSE;
1624 pts[0].x = xDst;
1625 pts[0].y = yDst;
1626 pts[1].x = xDst + widthDst;
1627 pts[1].y = yDst + heightDst;
1628 LPtoDP(devDst->hdc, pts, 2);
1629 xDst = pts[0].x;
1630 yDst = pts[0].y;
1631 widthDst = pts[1].x - pts[0].x;
1632 heightDst = pts[1].y - pts[0].y;
1634 pts[0].x = xSrc;
1635 pts[0].y = ySrc;
1636 pts[1].x = xSrc + widthSrc;
1637 pts[1].y = ySrc + heightSrc;
1638 LPtoDP(devSrc->hdc, pts, 2);
1639 xSrc = pts[0].x;
1640 ySrc = pts[0].y;
1641 widthSrc = pts[1].x - pts[0].x;
1642 heightSrc = pts[1].y - pts[0].y;
1643 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1645 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1646 if(widthDst != widthSrc || heightDst != heightSrc)
1647 #else
1648 if(!pXRenderSetPictureTransform)
1649 #endif
1651 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1652 return FALSE;
1655 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1657 static BOOL out = FALSE;
1658 if (!out)
1660 FIXME("not a dibsection\n");
1661 out = TRUE;
1663 return FALSE;
1666 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1667 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1669 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1670 SetLastError(ERROR_INVALID_PARAMETER);
1671 return FALSE;
1674 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1675 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1677 if(dib.dsBm.bmBitsPixel != 32) {
1678 FIXME("not a 32 bpp dibsection\n");
1679 return FALSE;
1681 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1683 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1684 top_down = TRUE;
1685 dstbits += widthSrc * (heightSrc - 1);
1686 y2 = ySrc;
1687 y = y2 + heightSrc - 1;
1689 else
1691 y = dib.dsBmih.biHeight - ySrc - 1;
1692 y2 = y - heightSrc + 1;
1695 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1697 for(; y >= y2; y--)
1699 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1700 widthSrc * 4);
1701 dstbits += (top_down ? -1 : 1) * widthSrc;
1704 else
1706 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1707 int x;
1709 for(; y >= y2; y--)
1711 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1712 for (x = 0; x < widthSrc; x++)
1714 DWORD argb = *srcbits++;
1715 argb = (argb & 0xffffff) | source_alpha;
1716 *dstbits++ = argb;
1718 if (top_down) /* we traversed the row forward so we should go back by two rows */
1719 dstbits -= 2 * widthSrc;
1724 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1726 wine_tsx11_lock();
1727 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1728 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1731 Avoid using XRenderFindStandardFormat as older libraries don't have it
1732 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1734 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1736 TRACE("src_format %p\n", src_format);
1738 pa.subwindow_mode = IncludeInferiors;
1740 /* FIXME use devDst->xrender->pict ? */
1741 dst_pict = pXRenderCreatePicture(gdi_display,
1742 devDst->drawable,
1743 pict_formats[dst_depth_type],
1744 CPSubwindowMode, &pa);
1745 TRACE("dst_pict %08lx\n", dst_pict);
1746 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1747 xpm = XCreatePixmap(gdi_display,
1748 root_window,
1749 widthSrc, heightSrc, 32);
1750 gcv.graphics_exposures = False;
1751 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1752 TRACE("xpm = %08lx\n", xpm);
1753 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1755 src_pict = pXRenderCreatePicture(gdi_display,
1756 xpm, src_format,
1757 CPSubwindowMode, &pa);
1758 TRACE("src_pict %08lx\n", src_pict);
1760 if (rgndata)
1762 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1763 devDst->dc_rect.left, devDst->dc_rect.top,
1764 (XRectangle *)rgndata->Buffer,
1765 rgndata->rdh.nCount );
1766 HeapFree( GetProcessHeap(), 0, rgndata );
1769 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1770 if(widthDst != widthSrc || heightDst != heightSrc) {
1771 double xscale = widthSrc/(double)widthDst;
1772 double yscale = heightSrc/(double)heightDst;
1773 XTransform xform = {{
1774 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1775 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1776 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1778 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1780 #endif
1781 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1782 0, 0, 0, 0,
1783 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1786 pXRenderFreePicture(gdi_display, src_pict);
1787 XFreePixmap(gdi_display, xpm);
1788 XFreeGC(gdi_display, gc);
1789 pXRenderFreePicture(gdi_display, dst_pict);
1790 image->data = NULL;
1791 XDestroyImage(image);
1793 wine_tsx11_unlock();
1794 HeapFree(GetProcessHeap(), 0, data);
1795 return TRUE;
1798 #else /* SONAME_LIBXRENDER */
1800 void X11DRV_XRender_Init(void)
1802 TRACE("XRender support not compiled in.\n");
1803 return;
1806 void X11DRV_XRender_Finalize(void)
1810 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1812 assert(0);
1813 return FALSE;
1816 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1818 assert(0);
1819 return;
1822 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1823 const RECT *lprect, LPCWSTR wstr, UINT count,
1824 const INT *lpDx )
1826 assert(0);
1827 return FALSE;
1830 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1832 assert(0);
1833 return;
1836 /******************************************************************************
1837 * AlphaBlend (x11drv.@)
1839 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1840 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1841 BLENDFUNCTION blendfn)
1843 FIXME("not supported - XRENDER headers were missing at compile time\n");
1844 return FALSE;
1847 #endif /* SONAME_LIBXRENDER */