msi: Fix buffer length value returned by MSI_RecordGetStringW on null and empty strings.
[wine/hacks.git] / dlls / winex11.drv / xrender.c
blobf4b68ffa5216ba7463f34b9c52394dc67a0cf778
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 XFORM xform;
58 SIZE devsize; /* size in device coords */
59 DWORD hash;
60 } LFANDSIZE;
62 #define INITIAL_REALIZED_BUF_SIZE 128
64 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
66 typedef struct
68 GlyphSet glyphset;
69 XRenderPictFormat *font_format;
70 int nrealized;
71 BOOL *realized;
72 void **bitmaps;
73 XGlyphInfo *gis;
74 } gsCacheEntryFormat;
76 typedef struct
78 LFANDSIZE lfsz;
79 AA_Type aa_default;
80 gsCacheEntryFormat * format[AA_MAXVALUE];
81 INT count;
82 INT next;
83 } gsCacheEntry;
85 struct tagXRENDERINFO
87 int cache_index;
88 Picture pict;
92 static gsCacheEntry *glyphsetCache = NULL;
93 static DWORD glyphsetCacheSize = 0;
94 static INT lastfree = -1;
95 static INT mru = -1;
97 #define INIT_CACHE_SIZE 10
99 static int antialias = 1;
101 static void *xrender_handle;
103 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
104 MAKE_FUNCPTR(XRenderAddGlyphs)
105 MAKE_FUNCPTR(XRenderComposite)
106 MAKE_FUNCPTR(XRenderCompositeString8)
107 MAKE_FUNCPTR(XRenderCompositeString16)
108 MAKE_FUNCPTR(XRenderCompositeString32)
109 MAKE_FUNCPTR(XRenderCompositeText16)
110 MAKE_FUNCPTR(XRenderCreateGlyphSet)
111 MAKE_FUNCPTR(XRenderCreatePicture)
112 MAKE_FUNCPTR(XRenderFillRectangle)
113 MAKE_FUNCPTR(XRenderFindFormat)
114 MAKE_FUNCPTR(XRenderFindVisualFormat)
115 MAKE_FUNCPTR(XRenderFreeGlyphSet)
116 MAKE_FUNCPTR(XRenderFreePicture)
117 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
118 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
119 MAKE_FUNCPTR(XRenderSetPictureTransform)
120 #endif
121 MAKE_FUNCPTR(XRenderQueryExtension)
122 #undef MAKE_FUNCPTR
124 static CRITICAL_SECTION xrender_cs;
125 static CRITICAL_SECTION_DEBUG critsect_debug =
127 0, 0, &xrender_cs,
128 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
129 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
131 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
133 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
134 ( ( (ULONG)_x4 << 24 ) | \
135 ( (ULONG)_x3 << 16 ) | \
136 ( (ULONG)_x2 << 8 ) | \
137 (ULONG)_x1 )
139 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
141 #define GASP_GRIDFIT 0x01
142 #define GASP_DOGRAY 0x02
144 #ifdef WORDS_BIGENDIAN
145 #define get_be_word(x) (x)
146 #define NATIVE_BYTE_ORDER MSBFirst
147 #else
148 #define get_be_word(x) RtlUshortByteSwap(x)
149 #define NATIVE_BYTE_ORDER LSBFirst
150 #endif
152 /***********************************************************************
153 * X11DRV_XRender_Init
155 * Let's see if our XServer has the extension available
158 void X11DRV_XRender_Init(void)
160 int event_base, i;
161 XRenderPictFormat pf;
163 if (client_side_with_render &&
164 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
165 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
166 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
169 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
170 LOAD_FUNCPTR(XRenderAddGlyphs)
171 LOAD_FUNCPTR(XRenderComposite)
172 LOAD_FUNCPTR(XRenderCompositeString8)
173 LOAD_FUNCPTR(XRenderCompositeString16)
174 LOAD_FUNCPTR(XRenderCompositeString32)
175 LOAD_FUNCPTR(XRenderCompositeText16)
176 LOAD_FUNCPTR(XRenderCreateGlyphSet)
177 LOAD_FUNCPTR(XRenderCreatePicture)
178 LOAD_FUNCPTR(XRenderFillRectangle)
179 LOAD_FUNCPTR(XRenderFindFormat)
180 LOAD_FUNCPTR(XRenderFindVisualFormat)
181 LOAD_FUNCPTR(XRenderFreeGlyphSet)
182 LOAD_FUNCPTR(XRenderFreePicture)
183 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
184 LOAD_FUNCPTR(XRenderQueryExtension)
185 #undef LOAD_FUNCPTR
186 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
187 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
188 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
189 #undef LOAD_OPTIONAL_FUNCPTR
190 #endif
193 wine_tsx11_lock();
194 if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
195 X11DRV_XRender_Installed = TRUE;
196 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
197 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
198 if(!pict_formats[color_drawable])
200 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
201 if (visual->class == DirectColor)
203 XVisualInfo info;
204 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
205 screen_depth, TrueColor, &info ))
207 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
208 if (pict_formats[color_drawable]) visual = info.visual;
212 if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
214 wine_tsx11_unlock();
215 WINE_MESSAGE(
216 "Wine has detected that you probably have a buggy version\n"
217 "of libXrender.so . Because of this client side font rendering\n"
218 "will be disabled. Please upgrade this library.\n");
219 X11DRV_XRender_Installed = FALSE;
220 return;
222 pf.type = PictTypeDirect;
223 pf.depth = 1;
224 pf.direct.alpha = 0;
225 pf.direct.alphaMask = 1;
226 pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
227 PictFormatDepth | PictFormatAlpha |
228 PictFormatAlphaMask, &pf, 0);
229 if(!pict_formats[mono_drawable]) {
230 ERR("mono_format == NULL?\n");
231 X11DRV_XRender_Installed = FALSE;
233 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
234 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
235 X11DRV_XRender_Installed = FALSE;
238 wine_tsx11_unlock();
241 sym_not_found:
242 if(X11DRV_XRender_Installed || client_side_with_core)
244 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
245 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
247 glyphsetCacheSize = INIT_CACHE_SIZE;
248 lastfree = 0;
249 for(i = 0; i < INIT_CACHE_SIZE; i++) {
250 glyphsetCache[i].next = i + 1;
251 glyphsetCache[i].count = -1;
253 glyphsetCache[i-1].next = -1;
254 using_client_side_fonts = 1;
256 if(!X11DRV_XRender_Installed) {
257 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
258 if(screen_depth <= 8 || !client_side_antialias_with_core)
259 antialias = 0;
260 } else {
261 if(screen_depth <= 8 || !client_side_antialias_with_render)
262 antialias = 0;
265 else TRACE("Using X11 core fonts\n");
268 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
270 if(p1->hash != p2->hash) return TRUE;
271 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
272 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
273 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
274 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
277 #if 0
278 static void walk_cache(void)
280 int i;
282 EnterCriticalSection(&xrender_cs);
283 for(i=mru; i >= 0; i = glyphsetCache[i].next)
284 TRACE("item %d\n", i);
285 LeaveCriticalSection(&xrender_cs);
287 #endif
289 static int LookupEntry(LFANDSIZE *plfsz)
291 int i, prev_i = -1;
293 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
294 TRACE("%d\n", i);
295 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
296 i = -1;
297 break;
300 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
301 glyphsetCache[i].count++;
302 if(prev_i >= 0) {
303 glyphsetCache[prev_i].next = glyphsetCache[i].next;
304 glyphsetCache[i].next = mru;
305 mru = i;
307 TRACE("found font in cache %d\n", i);
308 return i;
310 prev_i = i;
312 TRACE("font not in cache\n");
313 return -1;
316 static void FreeEntry(int entry)
318 int i, format;
320 for(format = 0; format < AA_MAXVALUE; format++) {
321 gsCacheEntryFormat * formatEntry;
323 if( !glyphsetCache[entry].format[format] )
324 continue;
326 formatEntry = glyphsetCache[entry].format[format];
328 if(formatEntry->glyphset) {
329 wine_tsx11_lock();
330 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
331 wine_tsx11_unlock();
332 formatEntry->glyphset = 0;
334 if(formatEntry->nrealized) {
335 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
336 formatEntry->realized = NULL;
337 if(formatEntry->bitmaps) {
338 for(i = 0; i < formatEntry->nrealized; i++)
339 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
340 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
341 formatEntry->bitmaps = NULL;
343 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
344 formatEntry->gis = NULL;
345 formatEntry->nrealized = 0;
348 HeapFree(GetProcessHeap(), 0, formatEntry);
349 glyphsetCache[entry].format[format] = NULL;
353 static int AllocEntry(void)
355 int best = -1, prev_best = -1, i, prev_i = -1;
357 if(lastfree >= 0) {
358 assert(glyphsetCache[lastfree].count == -1);
359 glyphsetCache[lastfree].count = 1;
360 best = lastfree;
361 lastfree = glyphsetCache[lastfree].next;
362 assert(best != mru);
363 glyphsetCache[best].next = mru;
364 mru = best;
366 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
367 return mru;
370 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
371 if(glyphsetCache[i].count == 0) {
372 best = i;
373 prev_best = prev_i;
375 prev_i = i;
378 if(best >= 0) {
379 TRACE("freeing unused glyphset at cache %d\n", best);
380 FreeEntry(best);
381 glyphsetCache[best].count = 1;
382 if(prev_best >= 0) {
383 glyphsetCache[prev_best].next = glyphsetCache[best].next;
384 glyphsetCache[best].next = mru;
385 mru = best;
386 } else {
387 assert(mru == best);
389 return mru;
392 TRACE("Growing cache\n");
394 if (glyphsetCache)
395 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
396 glyphsetCache,
397 (glyphsetCacheSize + INIT_CACHE_SIZE)
398 * sizeof(*glyphsetCache));
399 else
400 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
401 (glyphsetCacheSize + INIT_CACHE_SIZE)
402 * sizeof(*glyphsetCache));
404 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
405 i++) {
406 glyphsetCache[i].next = i + 1;
407 glyphsetCache[i].count = -1;
409 glyphsetCache[i-1].next = -1;
410 glyphsetCacheSize += INIT_CACHE_SIZE;
412 lastfree = glyphsetCache[best].next;
413 glyphsetCache[best].count = 1;
414 glyphsetCache[best].next = mru;
415 mru = best;
416 TRACE("new free cache slot at %d\n", mru);
417 return mru;
420 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
422 DWORD size;
423 WORD *gasp, *buffer;
424 WORD num_recs;
425 DWORD ppem;
426 TEXTMETRICW tm;
428 *flags = 0;
430 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
431 if(size == GDI_ERROR)
432 return FALSE;
434 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
435 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
437 GetTextMetricsW(physDev->hdc, &tm);
438 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
440 gasp++;
441 num_recs = get_be_word(*gasp);
442 gasp++;
443 while(num_recs--)
445 *flags = get_be_word(*(gasp + 1));
446 if(ppem <= get_be_word(*gasp))
447 break;
448 gasp += 2;
450 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
452 HeapFree(GetProcessHeap(), 0, buffer);
453 return TRUE;
456 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
458 AA_Type ret;
459 WORD flags;
460 UINT font_smoothing_type, font_smoothing_orientation;
462 if (X11DRV_XRender_Installed && subpixel &&
463 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
464 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
466 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
467 &font_smoothing_orientation, 0) &&
468 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
470 ret = AA_BGR;
472 else
473 ret = AA_RGB;
474 /*FIXME
475 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
476 But, Wine's subpixel rendering can support the portrait mode.
479 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
480 ret = AA_Grey;
481 else
482 ret = AA_None;
484 return ret;
487 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
489 int ret;
490 int format;
491 gsCacheEntry *entry;
492 static int hinter = -1;
493 static int subpixel = -1;
494 BOOL font_smoothing;
496 if((ret = LookupEntry(plfsz)) != -1) return ret;
498 ret = AllocEntry();
499 entry = glyphsetCache + ret;
500 entry->lfsz = *plfsz;
501 for( format = 0; format < AA_MAXVALUE; format++ ) {
502 assert( !entry->format[format] );
505 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
507 if(hinter == -1 || subpixel == -1)
509 RASTERIZER_STATUS status;
510 GetRasterizerCaps(&status, sizeof(status));
511 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
512 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
515 switch (plfsz->lf.lfQuality)
517 case ANTIALIASED_QUALITY:
518 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
519 break;
520 case CLEARTYPE_QUALITY:
521 case CLEARTYPE_NATURAL_QUALITY:
522 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
523 break;
524 case DEFAULT_QUALITY:
525 case DRAFT_QUALITY:
526 case PROOF_QUALITY:
527 default:
528 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
529 font_smoothing)
531 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
533 else
534 entry->aa_default = AA_None;
535 break;
538 else
539 entry->aa_default = AA_None;
541 return ret;
544 static void dec_ref_cache(int index)
546 assert(index >= 0);
547 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
548 assert(glyphsetCache[index].count > 0);
549 glyphsetCache[index].count--;
552 static void lfsz_calc_hash(LFANDSIZE *plfsz)
554 DWORD hash = 0, *ptr, two_chars;
555 WORD *pwc;
556 int i;
558 hash ^= plfsz->devsize.cx;
559 hash ^= plfsz->devsize.cy;
560 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
561 hash ^= *ptr;
562 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
563 hash ^= *ptr;
564 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
565 two_chars = *ptr;
566 pwc = (WCHAR *)&two_chars;
567 if(!*pwc) break;
568 *pwc = toupperW(*pwc);
569 pwc++;
570 *pwc = toupperW(*pwc);
571 hash ^= two_chars;
572 if(!*pwc) break;
574 plfsz->hash = hash;
575 return;
578 /***********************************************************************
579 * X11DRV_XRender_Finalize
581 void X11DRV_XRender_Finalize(void)
583 int i;
585 EnterCriticalSection(&xrender_cs);
586 for(i = mru; i >= 0; i = glyphsetCache[i].next)
587 FreeEntry(i);
588 LeaveCriticalSection(&xrender_cs);
592 /***********************************************************************
593 * X11DRV_XRender_SelectFont
595 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
597 LFANDSIZE lfsz;
599 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
600 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
601 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
602 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
603 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
604 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
605 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
606 GetWorldTransform( physDev->hdc, &lfsz.xform );
607 lfsz_calc_hash(&lfsz);
609 EnterCriticalSection(&xrender_cs);
610 if(!physDev->xrender) {
611 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
612 sizeof(*physDev->xrender));
613 physDev->xrender->cache_index = -1;
615 else if(physDev->xrender->cache_index != -1)
616 dec_ref_cache(physDev->xrender->cache_index);
617 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
618 LeaveCriticalSection(&xrender_cs);
619 return 0;
622 /***********************************************************************
623 * X11DRV_XRender_DeleteDC
625 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
627 X11DRV_XRender_UpdateDrawable(physDev);
629 EnterCriticalSection(&xrender_cs);
630 if(physDev->xrender->cache_index != -1)
631 dec_ref_cache(physDev->xrender->cache_index);
632 LeaveCriticalSection(&xrender_cs);
634 HeapFree(GetProcessHeap(), 0, physDev->xrender);
635 physDev->xrender = NULL;
636 return;
639 /***********************************************************************
640 * X11DRV_XRender_UpdateDrawable
642 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
643 * It deletes the pict and tile when the drawable changes.
645 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
647 wine_tsx11_lock();
649 if(physDev->xrender->pict)
651 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
652 XFlush(gdi_display);
653 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
654 physDev->xrender->pict = 0;
656 wine_tsx11_unlock();
658 return;
661 /************************************************************************
662 * UploadGlyph
664 * Helper to ExtTextOut. Must be called inside xrender_cs
666 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
668 unsigned int buflen;
669 char *buf;
670 Glyph gid;
671 GLYPHMETRICS gm;
672 XGlyphInfo gi;
673 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
674 gsCacheEntryFormat *formatEntry;
675 UINT ggo_format = GGO_GLYPH_INDEX;
676 XRenderPictFormat pf;
677 unsigned long pf_mask;
678 static const char zero[4];
679 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
681 switch(format) {
682 case AA_Grey:
683 ggo_format |= WINE_GGO_GRAY16_BITMAP;
684 break;
685 case AA_RGB:
686 ggo_format |= WINE_GGO_HRGB_BITMAP;
687 break;
688 case AA_BGR:
689 ggo_format |= WINE_GGO_HBGR_BITMAP;
690 break;
691 case AA_VRGB:
692 ggo_format |= WINE_GGO_VRGB_BITMAP;
693 break;
694 case AA_VBGR:
695 ggo_format |= WINE_GGO_VBGR_BITMAP;
696 break;
698 default:
699 ERR("aa = %d - not implemented\n", format);
700 case AA_None:
701 ggo_format |= GGO_BITMAP;
702 break;
705 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
706 if(buflen == GDI_ERROR) {
707 if(format != AA_None) {
708 format = AA_None;
709 entry->aa_default = AA_None;
710 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
711 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
713 if(buflen == GDI_ERROR) {
714 WARN("GetGlyphOutlineW failed\n");
715 return FALSE;
717 TRACE("Turning off antialiasing for this monochrome font\n");
720 /* If there is nothing for the current type, we create the entry. */
721 if( !entry->format[format] ) {
722 entry->format[format] = HeapAlloc(GetProcessHeap(),
723 HEAP_ZERO_MEMORY,
724 sizeof(gsCacheEntryFormat));
726 formatEntry = entry->format[format];
728 if(formatEntry->nrealized <= glyph) {
729 formatEntry->nrealized = (glyph / 128 + 1) * 128;
731 if (formatEntry->realized)
732 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
733 HEAP_ZERO_MEMORY,
734 formatEntry->realized,
735 formatEntry->nrealized * sizeof(BOOL));
736 else
737 formatEntry->realized = HeapAlloc(GetProcessHeap(),
738 HEAP_ZERO_MEMORY,
739 formatEntry->nrealized * sizeof(BOOL));
741 if(!X11DRV_XRender_Installed) {
742 if (formatEntry->bitmaps)
743 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
744 HEAP_ZERO_MEMORY,
745 formatEntry->bitmaps,
746 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
747 else
748 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
749 HEAP_ZERO_MEMORY,
750 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
752 if (formatEntry->gis)
753 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
754 HEAP_ZERO_MEMORY,
755 formatEntry->gis,
756 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
757 else
758 formatEntry->gis = HeapAlloc(GetProcessHeap(),
759 HEAP_ZERO_MEMORY,
760 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
764 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
765 switch(format) {
766 case AA_Grey:
767 pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
768 pf.type = PictTypeDirect;
769 pf.depth = 8;
770 pf.direct.alpha = 0;
771 pf.direct.alphaMask = 0xff;
772 break;
774 case AA_RGB:
775 case AA_BGR:
776 case AA_VRGB:
777 case AA_VBGR:
778 pf_mask = PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask |
779 PictFormatGreen | PictFormatGreenMask | PictFormatBlue |
780 PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask;
781 pf.type = PictTypeDirect;
782 pf.depth = 32;
783 pf.direct.red = 16;
784 pf.direct.redMask = 0xff;
785 pf.direct.green = 8;
786 pf.direct.greenMask = 0xff;
787 pf.direct.blue = 0;
788 pf.direct.blueMask = 0xff;
789 pf.direct.alpha = 24;
790 pf.direct.alphaMask = 0xff;
791 break;
793 default:
794 ERR("aa = %d - not implemented\n", format);
795 case AA_None:
796 pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
797 pf.type = PictTypeDirect;
798 pf.depth = 1;
799 pf.direct.alpha = 0;
800 pf.direct.alphaMask = 1;
801 break;
804 wine_tsx11_lock();
805 formatEntry->font_format = pXRenderFindFormat(gdi_display, pf_mask, &pf, 0);
806 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
807 wine_tsx11_unlock();
811 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
812 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
813 formatEntry->realized[glyph] = TRUE;
815 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
816 buflen,
817 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
818 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
820 gi.width = gm.gmBlackBoxX;
821 gi.height = gm.gmBlackBoxY;
822 gi.x = -gm.gmptGlyphOrigin.x;
823 gi.y = gm.gmptGlyphOrigin.y;
824 gi.xOff = gm.gmCellIncX;
825 gi.yOff = gm.gmCellIncY;
827 if(TRACE_ON(xrender)) {
828 int pitch, i, j;
829 char output[300];
830 unsigned char *line;
832 if(format == AA_None) {
833 pitch = ((gi.width + 31) / 32) * 4;
834 for(i = 0; i < gi.height; i++) {
835 line = (unsigned char*) buf + i * pitch;
836 output[0] = '\0';
837 for(j = 0; j < pitch * 8; j++) {
838 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
840 TRACE("%s\n", output);
842 } else {
843 static const char blks[] = " .:;!o*#";
844 char str[2];
846 str[1] = '\0';
847 pitch = ((gi.width + 3) / 4) * 4;
848 for(i = 0; i < gi.height; i++) {
849 line = (unsigned char*) buf + i * pitch;
850 output[0] = '\0';
851 for(j = 0; j < pitch; j++) {
852 str[0] = blks[line[j] >> 5];
853 strcat(output, str);
855 TRACE("%s\n", output);
861 if(formatEntry->glyphset) {
862 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
863 unsigned char *byte = (unsigned char*) buf, c;
864 int i = buflen;
866 while(i--) {
867 c = *byte;
869 /* magic to flip bit order */
870 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
871 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
872 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
874 *byte++ = c;
877 else if ( format != AA_Grey &&
878 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
880 unsigned int i, *data = (unsigned int *)buf;
881 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
883 gid = glyph;
886 XRenderCompositeText seems to ignore 0x0 glyphs when
887 AA_None, which means we lose the advance width of glyphs
888 like the space. We'll pretend that such glyphs are 1x1
889 bitmaps.
892 if(buflen == 0)
893 gi.width = gi.height = 1;
895 wine_tsx11_lock();
896 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
897 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
898 wine_tsx11_unlock();
899 HeapFree(GetProcessHeap(), 0, buf);
900 } else {
901 formatEntry->bitmaps[glyph] = buf;
904 formatEntry->gis[glyph] = gi;
906 return TRUE;
909 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
910 void *bitmap, XGlyphInfo *gi)
912 unsigned char *srcLine = bitmap, *src;
913 unsigned char bits, bitsMask;
914 int width = gi->width;
915 int stride = ((width + 31) & ~31) >> 3;
916 int height = gi->height;
917 int w;
918 int xspan, lenspan;
920 TRACE("%d, %d\n", x, y);
921 x -= gi->x;
922 y -= gi->y;
923 while (height--)
925 src = srcLine;
926 srcLine += stride;
927 w = width;
929 bitsMask = 0x80; /* FreeType is always MSB first */
930 bits = *src++;
932 xspan = x;
933 while (w)
935 if (bits & bitsMask)
937 lenspan = 0;
940 lenspan++;
941 if (lenspan == w)
942 break;
943 bitsMask = bitsMask >> 1;
944 if (!bitsMask)
946 bits = *src++;
947 bitsMask = 0x80;
949 } while (bits & bitsMask);
950 XFillRectangle (gdi_display, physDev->drawable,
951 physDev->gc, xspan, y, lenspan, 1);
952 xspan += lenspan;
953 w -= lenspan;
955 else
959 w--;
960 xspan++;
961 if (!w)
962 break;
963 bitsMask = bitsMask >> 1;
964 if (!bitsMask)
966 bits = *src++;
967 bitsMask = 0x80;
969 } while (!(bits & bitsMask));
972 y++;
976 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
977 void *bitmap, XGlyphInfo *gi)
979 unsigned char *srcLine = bitmap, *src, bits;
980 int width = gi->width;
981 int stride = ((width + 3) & ~3);
982 int height = gi->height;
983 int w;
984 int xspan, lenspan;
986 x -= gi->x;
987 y -= gi->y;
988 while (height--)
990 src = srcLine;
991 srcLine += stride;
992 w = width;
994 bits = *src++;
995 xspan = x;
996 while (w)
998 if (bits >= 0x80)
1000 lenspan = 0;
1003 lenspan++;
1004 if (lenspan == w)
1005 break;
1006 bits = *src++;
1007 } while (bits >= 0x80);
1008 XFillRectangle (gdi_display, physDev->drawable,
1009 physDev->gc, xspan, y, lenspan, 1);
1010 xspan += lenspan;
1011 w -= lenspan;
1013 else
1017 w--;
1018 xspan++;
1019 if (!w)
1020 break;
1021 bits = *src++;
1022 } while (bits < 0x80);
1025 y++;
1030 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1032 int s, l;
1034 s = 0;
1035 while ((mask & 1) == 0)
1037 mask >>= 1;
1038 s++;
1040 l = 0;
1041 while ((mask & 1) == 1)
1043 mask >>= 1;
1044 l++;
1046 *shift = s;
1047 *len = l;
1050 static DWORD GetField (DWORD pixel, int shift, int len)
1052 pixel = pixel & (((1 << (len)) - 1) << shift);
1053 pixel = pixel << (32 - (shift + len)) >> 24;
1054 while (len < 8)
1056 pixel |= (pixel >> len);
1057 len <<= 1;
1059 return pixel;
1063 static DWORD PutField (DWORD pixel, int shift, int len)
1065 shift = shift - (8 - len);
1066 if (len <= 8)
1067 pixel &= (((1 << len) - 1) << (8 - len));
1068 if (shift < 0)
1069 pixel >>= -shift;
1070 else
1071 pixel <<= shift;
1072 return pixel;
1075 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1076 int color)
1078 int r_shift, r_len;
1079 int g_shift, g_len;
1080 int b_shift, b_len;
1081 BYTE *maskLine, *mask, m;
1082 int maskStride;
1083 DWORD pixel;
1084 int width, height;
1085 int w, tx;
1086 BYTE src_r, src_g, src_b;
1088 x -= gi->x;
1089 y -= gi->y;
1090 width = gi->width;
1091 height = gi->height;
1093 maskLine = bitmap;
1094 maskStride = (width + 3) & ~3;
1096 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1097 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1098 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1100 src_r = GetField(color, r_shift, r_len);
1101 src_g = GetField(color, g_shift, g_len);
1102 src_b = GetField(color, b_shift, b_len);
1104 for(; height--; y++)
1106 mask = maskLine;
1107 maskLine += maskStride;
1108 w = width;
1109 tx = x;
1111 if(y < 0) continue;
1112 if(y >= image->height) break;
1114 for(; w--; tx++)
1116 if(tx >= image->width) break;
1118 m = *mask++;
1119 if(tx < 0) continue;
1121 if (m == 0xff)
1122 XPutPixel (image, tx, y, color);
1123 else if (m)
1125 BYTE r, g, b;
1127 pixel = XGetPixel (image, tx, y);
1129 r = GetField(pixel, r_shift, r_len);
1130 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1131 g = GetField(pixel, g_shift, g_len);
1132 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1133 b = GetField(pixel, b_shift, b_len);
1134 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1136 pixel = (PutField (r, r_shift, r_len) |
1137 PutField (g, g_shift, g_len) |
1138 PutField (b, b_shift, b_len));
1139 XPutPixel (image, tx, y, pixel);
1145 /*************************************************************
1146 * get_tile_pict
1148 * Returns an appropriate Picture for tiling the text colour.
1149 * Call and use result within the xrender_cs
1151 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1153 static struct
1155 Pixmap xpm;
1156 Picture pict;
1157 int current_color;
1158 } tiles[2], *tile;
1159 XRenderColor col;
1161 tile = &tiles[type];
1163 if(!tile->xpm)
1165 XRenderPictureAttributes pa;
1167 wine_tsx11_lock();
1168 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1170 pa.repeat = True;
1171 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1172 wine_tsx11_unlock();
1174 /* init current_color to something different from text_pixel */
1175 tile->current_color = ~text_pixel;
1177 if(type == mono_drawable)
1179 /* for a 1bpp bitmap we always need a 1 in the tile */
1180 col.red = col.green = col.blue = 0;
1181 col.alpha = 0xffff;
1182 wine_tsx11_lock();
1183 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1184 wine_tsx11_unlock();
1188 if(text_pixel != tile->current_color && type == color_drawable)
1190 /* Map 0 -- 0xff onto 0 -- 0xffff */
1191 int r_shift, r_len;
1192 int g_shift, g_len;
1193 int b_shift, b_len;
1195 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1196 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1197 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1199 col.red = GetField(text_pixel, r_shift, r_len);
1200 col.red |= col.red << 8;
1201 col.green = GetField(text_pixel, g_shift, g_len);
1202 col.green |= col.green << 8;
1203 col.blue = GetField(text_pixel, b_shift, b_len);
1204 col.blue |= col.blue << 8;
1205 col.alpha = 0xffff;
1207 wine_tsx11_lock();
1208 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1209 wine_tsx11_unlock();
1210 tile->current_color = text_pixel;
1212 return tile->pict;
1215 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1217 return 1;
1220 /***********************************************************************
1221 * X11DRV_XRender_ExtTextOut
1223 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1224 const RECT *lprect, LPCWSTR wstr, UINT count,
1225 const INT *lpDx )
1227 RGNDATA *data;
1228 XGCValues xgcval;
1229 gsCacheEntry *entry;
1230 gsCacheEntryFormat *formatEntry;
1231 BOOL retv = FALSE;
1232 HDC hdc = physDev->hdc;
1233 int textPixel, backgroundPixel;
1234 HRGN saved_region = 0;
1235 BOOL disable_antialias = FALSE;
1236 AA_Type aa_type = AA_None;
1237 DIBSECTION bmp;
1238 unsigned int idx;
1239 double cosEsc, sinEsc;
1240 LOGFONTW lf;
1241 enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1242 Picture tile_pict = 0;
1244 /* Do we need to disable antialiasing because of palette mode? */
1245 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1246 TRACE("bitmap is not a DIB\n");
1248 else if (bmp.dsBmih.biBitCount <= 8) {
1249 TRACE("Disabling antialiasing\n");
1250 disable_antialias = TRUE;
1253 xgcval.function = GXcopy;
1254 xgcval.background = physDev->backgroundPixel;
1255 xgcval.fill_style = FillSolid;
1256 wine_tsx11_lock();
1257 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1258 wine_tsx11_unlock();
1260 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1262 if(physDev->depth == 1) {
1263 if((physDev->textPixel & 0xffffff) == 0) {
1264 textPixel = 0;
1265 backgroundPixel = 1;
1266 } else {
1267 textPixel = 1;
1268 backgroundPixel = 0;
1270 } else {
1271 textPixel = physDev->textPixel;
1272 backgroundPixel = physDev->backgroundPixel;
1275 if(flags & ETO_OPAQUE)
1277 wine_tsx11_lock();
1278 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1279 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1280 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1281 lprect->right - lprect->left, lprect->bottom - lprect->top );
1282 wine_tsx11_unlock();
1285 if(count == 0)
1287 retv = TRUE;
1288 goto done_unlock;
1292 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1293 if(lf.lfEscapement != 0) {
1294 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1295 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1296 } else {
1297 cosEsc = 1;
1298 sinEsc = 0;
1301 if (flags & ETO_CLIPPED)
1303 HRGN clip_region;
1305 clip_region = CreateRectRgnIndirect( lprect );
1306 /* make a copy of the current device region */
1307 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1308 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1309 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1310 DeleteObject( clip_region );
1313 if(X11DRV_XRender_Installed) {
1314 if(!physDev->xrender->pict) {
1315 XRenderPictureAttributes pa;
1316 pa.subwindow_mode = IncludeInferiors;
1318 wine_tsx11_lock();
1319 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1320 physDev->drawable,
1321 pict_formats[depth_type],
1322 CPSubwindowMode, &pa);
1323 wine_tsx11_unlock();
1325 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1326 physDev->xrender->pict, hdc, physDev->drawable);
1327 } else {
1328 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1329 physDev->xrender->pict, hdc, physDev->drawable);
1332 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1334 wine_tsx11_lock();
1335 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1336 physDev->dc_rect.left, physDev->dc_rect.top,
1337 (XRectangle *)data->Buffer, data->rdh.nCount );
1338 wine_tsx11_unlock();
1339 HeapFree( GetProcessHeap(), 0, data );
1343 EnterCriticalSection(&xrender_cs);
1345 entry = glyphsetCache + physDev->xrender->cache_index;
1346 if( disable_antialias == FALSE )
1347 aa_type = entry->aa_default;
1348 formatEntry = entry->format[aa_type];
1350 for(idx = 0; idx < count; idx++) {
1351 if( !formatEntry ) {
1352 UploadGlyph(physDev, wstr[idx], aa_type);
1353 /* re-evaluate antialias since aa_default may have changed */
1354 if( disable_antialias == FALSE )
1355 aa_type = entry->aa_default;
1356 formatEntry = entry->format[aa_type];
1357 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1358 UploadGlyph(physDev, wstr[idx], aa_type);
1361 if (!formatEntry)
1363 WARN("could not upload requested glyphs\n");
1364 LeaveCriticalSection(&xrender_cs);
1365 goto done_unlock;
1368 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1369 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1371 if(X11DRV_XRender_Installed)
1373 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1374 INT offset = 0;
1375 POINT desired, current;
1376 int render_op = PictOpOver;
1378 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1379 So we pass zeros to the function and move to our starting position using the first
1380 element of the elts array. */
1382 desired.x = physDev->dc_rect.left + x;
1383 desired.y = physDev->dc_rect.top + y;
1384 current.x = current.y = 0;
1386 tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1388 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1390 if((depth_type == mono_drawable) && (textPixel == 0))
1391 render_op = PictOpOutReverse; /* This gives us 'black' text */
1393 for(idx = 0; idx < count; idx++)
1395 elts[idx].glyphset = formatEntry->glyphset;
1396 elts[idx].chars = wstr + idx;
1397 elts[idx].nchars = 1;
1398 elts[idx].xOff = desired.x - current.x;
1399 elts[idx].yOff = desired.y - current.y;
1401 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1402 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1404 if(!lpDx)
1406 desired.x += formatEntry->gis[wstr[idx]].xOff;
1407 desired.y += formatEntry->gis[wstr[idx]].yOff;
1409 else
1411 offset += lpDx[idx];
1412 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1413 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1416 wine_tsx11_lock();
1417 pXRenderCompositeText16(gdi_display, render_op,
1418 tile_pict,
1419 physDev->xrender->pict,
1420 formatEntry->font_format,
1421 0, 0, 0, 0, elts, count);
1422 wine_tsx11_unlock();
1423 HeapFree(GetProcessHeap(), 0, elts);
1424 } else {
1425 INT offset = 0, xoff = 0, yoff = 0;
1426 wine_tsx11_lock();
1427 XSetForeground( gdi_display, physDev->gc, textPixel );
1429 if(aa_type == AA_None || physDev->depth == 1)
1431 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1433 if(aa_type == AA_None)
1434 sharp_glyph_fn = SharpGlyphMono;
1435 else
1436 sharp_glyph_fn = SharpGlyphGray;
1438 for(idx = 0; idx < count; idx++) {
1439 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1440 physDev->dc_rect.top + y + yoff,
1441 formatEntry->bitmaps[wstr[idx]],
1442 &formatEntry->gis[wstr[idx]]);
1443 if(lpDx) {
1444 offset += lpDx[idx];
1445 xoff = offset * cosEsc;
1446 yoff = offset * -sinEsc;
1447 } else {
1448 xoff += formatEntry->gis[wstr[idx]].xOff;
1449 yoff += formatEntry->gis[wstr[idx]].yOff;
1452 } else {
1453 XImage *image;
1454 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1455 RECT extents = {0, 0, 0, 0};
1456 POINT cur = {0, 0};
1457 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1458 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1460 TRACE("drawable %dx%d\n", w, h);
1462 for(idx = 0; idx < count; idx++) {
1463 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1464 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1465 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1466 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1467 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1468 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1469 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1470 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1471 if(lpDx) {
1472 offset += lpDx[idx];
1473 cur.x = offset * cosEsc;
1474 cur.y = offset * -sinEsc;
1475 } else {
1476 cur.x += formatEntry->gis[wstr[idx]].xOff;
1477 cur.y += formatEntry->gis[wstr[idx]].yOff;
1480 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1481 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1483 if(physDev->dc_rect.left + x + extents.left >= 0) {
1484 image_x = physDev->dc_rect.left + x + extents.left;
1485 image_off_x = 0;
1486 } else {
1487 image_x = 0;
1488 image_off_x = physDev->dc_rect.left + x + extents.left;
1490 if(physDev->dc_rect.top + y + extents.top >= 0) {
1491 image_y = physDev->dc_rect.top + y + extents.top;
1492 image_off_y = 0;
1493 } else {
1494 image_y = 0;
1495 image_off_y = physDev->dc_rect.top + y + extents.top;
1497 if(physDev->dc_rect.left + x + extents.right < w)
1498 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1499 else
1500 image_w = w - image_x;
1501 if(physDev->dc_rect.top + y + extents.bottom < h)
1502 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1503 else
1504 image_h = h - image_y;
1506 if(image_w <= 0 || image_h <= 0) goto no_image;
1508 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1509 image = XGetImage(gdi_display, physDev->drawable,
1510 image_x, image_y, image_w, image_h,
1511 AllPlanes, ZPixmap);
1512 X11DRV_check_error();
1514 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1515 gdi_display, (int)physDev->drawable, image_x, image_y,
1516 image_w, image_h, AllPlanes, ZPixmap,
1517 physDev->depth, image);
1518 if(!image) {
1519 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1520 physDev->depth);
1521 GC gc;
1522 XGCValues gcv;
1524 gcv.graphics_exposures = False;
1525 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1526 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1527 image_w, image_h, 0, 0);
1528 XFreeGC(gdi_display, gc);
1529 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1530 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1531 ZPixmap);
1532 X11DRV_check_error();
1533 XFreePixmap(gdi_display, xpm);
1535 if(!image) goto no_image;
1537 image->red_mask = visual->red_mask;
1538 image->green_mask = visual->green_mask;
1539 image->blue_mask = visual->blue_mask;
1541 offset = xoff = yoff = 0;
1542 for(idx = 0; idx < count; idx++) {
1543 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1544 yoff + image_off_y - extents.top,
1545 formatEntry->bitmaps[wstr[idx]],
1546 &formatEntry->gis[wstr[idx]],
1547 physDev->textPixel);
1548 if(lpDx) {
1549 offset += lpDx[idx];
1550 xoff = offset * cosEsc;
1551 yoff = offset * -sinEsc;
1552 } else {
1553 xoff += formatEntry->gis[wstr[idx]].xOff;
1554 yoff += formatEntry->gis[wstr[idx]].yOff;
1557 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1558 image_x, image_y, image_w, image_h);
1559 XDestroyImage(image);
1561 no_image:
1562 wine_tsx11_unlock();
1564 LeaveCriticalSection(&xrender_cs);
1566 if (flags & ETO_CLIPPED)
1568 /* restore the device region */
1569 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1570 DeleteObject( saved_region );
1573 retv = TRUE;
1575 done_unlock:
1576 X11DRV_UnlockDIBSection( physDev, TRUE );
1577 return retv;
1580 /******************************************************************************
1581 * AlphaBlend (x11drv.@)
1583 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1584 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1585 BLENDFUNCTION blendfn)
1587 XRenderPictureAttributes pa;
1588 XRenderPictFormat *src_format;
1589 XRenderPictFormat argb32_templ = {
1590 0, /* id */
1591 PictTypeDirect, /* type */
1592 32, /* depth */
1593 { /* direct */
1594 16, /* direct.red */
1595 0xff, /* direct.redMask */
1596 8, /* direct.green */
1597 0xff, /* direct.greenMask */
1598 0, /* direct.blue */
1599 0xff, /* direct.blueMask */
1600 24, /* direct.alpha */
1601 0xff, /* direct.alphaMask */
1603 0, /* colormap */
1605 unsigned long argb32_templ_mask =
1606 PictFormatType |
1607 PictFormatDepth |
1608 PictFormatRed |
1609 PictFormatRedMask |
1610 PictFormatGreen |
1611 PictFormatGreenMask |
1612 PictFormatBlue |
1613 PictFormatBlueMask |
1614 PictFormatAlpha |
1615 PictFormatAlphaMask;
1617 Picture dst_pict, src_pict;
1618 Pixmap xpm;
1619 DIBSECTION dib;
1620 XImage *image;
1621 GC gc;
1622 XGCValues gcv;
1623 DWORD *dstbits, *data;
1624 int y, y2;
1625 POINT pts[2];
1626 BOOL top_down = FALSE;
1627 RGNDATA *rgndata;
1628 enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1630 if(!X11DRV_XRender_Installed) {
1631 FIXME("Unable to AlphaBlend without Xrender\n");
1632 return FALSE;
1634 pts[0].x = xDst;
1635 pts[0].y = yDst;
1636 pts[1].x = xDst + widthDst;
1637 pts[1].y = yDst + heightDst;
1638 LPtoDP(devDst->hdc, pts, 2);
1639 xDst = pts[0].x;
1640 yDst = pts[0].y;
1641 widthDst = pts[1].x - pts[0].x;
1642 heightDst = pts[1].y - pts[0].y;
1644 pts[0].x = xSrc;
1645 pts[0].y = ySrc;
1646 pts[1].x = xSrc + widthSrc;
1647 pts[1].y = ySrc + heightSrc;
1648 LPtoDP(devSrc->hdc, pts, 2);
1649 xSrc = pts[0].x;
1650 ySrc = pts[0].y;
1651 widthSrc = pts[1].x - pts[0].x;
1652 heightSrc = pts[1].y - pts[0].y;
1653 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1655 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1656 if(widthDst != widthSrc || heightDst != heightSrc)
1657 #else
1658 if(!pXRenderSetPictureTransform)
1659 #endif
1661 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1662 return FALSE;
1665 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1667 static BOOL out = FALSE;
1668 if (!out)
1670 FIXME("not a dibsection\n");
1671 out = TRUE;
1673 return FALSE;
1676 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1677 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1679 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1680 SetLastError(ERROR_INVALID_PARAMETER);
1681 return FALSE;
1684 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1685 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1687 if(dib.dsBm.bmBitsPixel != 32) {
1688 FIXME("not a 32 bpp dibsection\n");
1689 return FALSE;
1691 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1693 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1694 top_down = TRUE;
1695 dstbits += widthSrc * (heightSrc - 1);
1696 y2 = ySrc;
1697 y = y2 + heightSrc - 1;
1699 else
1701 y = dib.dsBmih.biHeight - ySrc - 1;
1702 y2 = y - heightSrc + 1;
1705 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1707 for(; y >= y2; y--)
1709 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1710 widthSrc * 4);
1711 dstbits += (top_down ? -1 : 1) * widthSrc;
1714 else
1716 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1717 int x;
1719 for(; y >= y2; y--)
1721 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1722 for (x = 0; x < widthSrc; x++)
1724 DWORD argb = *srcbits++;
1725 argb = (argb & 0xffffff) | source_alpha;
1726 *dstbits++ = argb;
1728 if (top_down) /* we traversed the row forward so we should go back by two rows */
1729 dstbits -= 2 * widthSrc;
1734 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1736 wine_tsx11_lock();
1737 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1738 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1741 Avoid using XRenderFindStandardFormat as older libraries don't have it
1742 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1744 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1746 TRACE("src_format %p\n", src_format);
1748 pa.subwindow_mode = IncludeInferiors;
1750 /* FIXME use devDst->xrender->pict ? */
1751 dst_pict = pXRenderCreatePicture(gdi_display,
1752 devDst->drawable,
1753 pict_formats[dst_depth_type],
1754 CPSubwindowMode, &pa);
1755 TRACE("dst_pict %08lx\n", dst_pict);
1756 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1757 xpm = XCreatePixmap(gdi_display,
1758 root_window,
1759 widthSrc, heightSrc, 32);
1760 gcv.graphics_exposures = False;
1761 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1762 TRACE("xpm = %08lx\n", xpm);
1763 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1765 src_pict = pXRenderCreatePicture(gdi_display,
1766 xpm, src_format,
1767 CPSubwindowMode, &pa);
1768 TRACE("src_pict %08lx\n", src_pict);
1770 if (rgndata)
1772 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1773 devDst->dc_rect.left, devDst->dc_rect.top,
1774 (XRectangle *)rgndata->Buffer,
1775 rgndata->rdh.nCount );
1776 HeapFree( GetProcessHeap(), 0, rgndata );
1779 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1780 if(widthDst != widthSrc || heightDst != heightSrc) {
1781 double xscale = widthSrc/(double)widthDst;
1782 double yscale = heightSrc/(double)heightDst;
1783 XTransform xform = {{
1784 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1785 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1786 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1788 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1790 #endif
1791 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1792 0, 0, 0, 0,
1793 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1796 pXRenderFreePicture(gdi_display, src_pict);
1797 XFreePixmap(gdi_display, xpm);
1798 XFreeGC(gdi_display, gc);
1799 pXRenderFreePicture(gdi_display, dst_pict);
1800 image->data = NULL;
1801 XDestroyImage(image);
1803 wine_tsx11_unlock();
1804 HeapFree(GetProcessHeap(), 0, data);
1805 return TRUE;
1808 #else /* SONAME_LIBXRENDER */
1810 void X11DRV_XRender_Init(void)
1812 TRACE("XRender support not compiled in.\n");
1813 return;
1816 void X11DRV_XRender_Finalize(void)
1820 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1822 assert(0);
1823 return FALSE;
1826 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1828 assert(0);
1829 return;
1832 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1833 const RECT *lprect, LPCWSTR wstr, UINT count,
1834 const INT *lpDx )
1836 assert(0);
1837 return FALSE;
1840 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1842 assert(0);
1843 return;
1846 /******************************************************************************
1847 * AlphaBlend (x11drv.@)
1849 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1850 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1851 BLENDFUNCTION blendfn)
1853 FIXME("not supported - XRENDER headers were missing at compile time\n");
1854 return FALSE;
1857 #endif /* SONAME_LIBXRENDER */