save old text color during a call of DrawCaptionTempW
[wine/kumbayo.git] / dlls / winex11.drv / xrender.c
blobc5b4af57745487ce7241a4171bf34ead80c2babc
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 #else
146 #define get_be_word(x) RtlUshortByteSwap(x)
147 #endif
149 /***********************************************************************
150 * X11DRV_XRender_Init
152 * Let's see if our XServer has the extension available
155 void X11DRV_XRender_Init(void)
157 int event_base, i;
158 XRenderPictFormat pf;
160 if (client_side_with_render &&
161 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
162 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
163 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
166 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
167 LOAD_FUNCPTR(XRenderAddGlyphs)
168 LOAD_FUNCPTR(XRenderComposite)
169 LOAD_FUNCPTR(XRenderCompositeString8)
170 LOAD_FUNCPTR(XRenderCompositeString16)
171 LOAD_FUNCPTR(XRenderCompositeString32)
172 LOAD_FUNCPTR(XRenderCompositeText16)
173 LOAD_FUNCPTR(XRenderCreateGlyphSet)
174 LOAD_FUNCPTR(XRenderCreatePicture)
175 LOAD_FUNCPTR(XRenderFillRectangle)
176 LOAD_FUNCPTR(XRenderFindFormat)
177 LOAD_FUNCPTR(XRenderFindVisualFormat)
178 LOAD_FUNCPTR(XRenderFreeGlyphSet)
179 LOAD_FUNCPTR(XRenderFreePicture)
180 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
181 LOAD_FUNCPTR(XRenderQueryExtension)
182 #undef LOAD_FUNCPTR
183 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
184 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
185 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
186 #undef LOAD_OPTIONAL_FUNCPTR
187 #endif
190 wine_tsx11_lock();
191 if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
192 X11DRV_XRender_Installed = TRUE;
193 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
194 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
195 if(!pict_formats[color_drawable])
197 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
198 if (visual->class == DirectColor)
200 XVisualInfo info;
201 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
202 screen_depth, TrueColor, &info ))
204 pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
205 if (pict_formats[color_drawable]) visual = info.visual;
209 if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
211 wine_tsx11_unlock();
212 WINE_MESSAGE(
213 "Wine has detected that you probably have a buggy version\n"
214 "of libXrender.so . Because of this client side font rendering\n"
215 "will be disabled. Please upgrade this library.\n");
216 X11DRV_XRender_Installed = FALSE;
217 return;
219 pf.type = PictTypeDirect;
220 pf.depth = 1;
221 pf.direct.alpha = 0;
222 pf.direct.alphaMask = 1;
223 pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
224 PictFormatDepth | PictFormatAlpha |
225 PictFormatAlphaMask, &pf, 0);
226 if(!pict_formats[mono_drawable]) {
227 ERR("mono_format == NULL?\n");
228 X11DRV_XRender_Installed = FALSE;
230 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
231 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
232 X11DRV_XRender_Installed = FALSE;
235 wine_tsx11_unlock();
238 sym_not_found:
239 if(X11DRV_XRender_Installed || client_side_with_core)
241 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
242 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
244 glyphsetCacheSize = INIT_CACHE_SIZE;
245 lastfree = 0;
246 for(i = 0; i < INIT_CACHE_SIZE; i++) {
247 glyphsetCache[i].next = i + 1;
248 glyphsetCache[i].count = -1;
250 glyphsetCache[i-1].next = -1;
251 using_client_side_fonts = 1;
253 if(!X11DRV_XRender_Installed) {
254 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
255 if(screen_depth <= 8 || !client_side_antialias_with_core)
256 antialias = 0;
257 } else {
258 if(screen_depth <= 8 || !client_side_antialias_with_render)
259 antialias = 0;
262 else TRACE("Using X11 core fonts\n");
265 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
267 if(p1->hash != p2->hash) return TRUE;
268 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
269 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
270 return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
273 #if 0
274 static void walk_cache(void)
276 int i;
278 EnterCriticalSection(&xrender_cs);
279 for(i=mru; i >= 0; i = glyphsetCache[i].next)
280 TRACE("item %d\n", i);
281 LeaveCriticalSection(&xrender_cs);
283 #endif
285 static int LookupEntry(LFANDSIZE *plfsz)
287 int i, prev_i = -1;
289 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
290 TRACE("%d\n", i);
291 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
292 i = -1;
293 break;
296 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
297 glyphsetCache[i].count++;
298 if(prev_i >= 0) {
299 glyphsetCache[prev_i].next = glyphsetCache[i].next;
300 glyphsetCache[i].next = mru;
301 mru = i;
303 TRACE("found font in cache %d\n", i);
304 return i;
306 prev_i = i;
308 TRACE("font not in cache\n");
309 return -1;
312 static void FreeEntry(int entry)
314 int i, format;
316 for(format = 0; format < AA_MAXVALUE; format++) {
317 gsCacheEntryFormat * formatEntry;
319 if( !glyphsetCache[entry].format[format] )
320 continue;
322 formatEntry = glyphsetCache[entry].format[format];
324 if(formatEntry->glyphset) {
325 wine_tsx11_lock();
326 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
327 wine_tsx11_unlock();
328 formatEntry->glyphset = 0;
330 if(formatEntry->nrealized) {
331 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
332 formatEntry->realized = NULL;
333 if(formatEntry->bitmaps) {
334 for(i = 0; i < formatEntry->nrealized; i++)
335 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
336 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
337 formatEntry->bitmaps = NULL;
339 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
340 formatEntry->gis = NULL;
341 formatEntry->nrealized = 0;
344 HeapFree(GetProcessHeap(), 0, formatEntry);
345 glyphsetCache[entry].format[format] = NULL;
349 static int AllocEntry(void)
351 int best = -1, prev_best = -1, i, prev_i = -1;
353 if(lastfree >= 0) {
354 assert(glyphsetCache[lastfree].count == -1);
355 glyphsetCache[lastfree].count = 1;
356 best = lastfree;
357 lastfree = glyphsetCache[lastfree].next;
358 assert(best != mru);
359 glyphsetCache[best].next = mru;
360 mru = best;
362 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
363 return mru;
366 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
367 if(glyphsetCache[i].count == 0) {
368 best = i;
369 prev_best = prev_i;
371 prev_i = i;
374 if(best >= 0) {
375 TRACE("freeing unused glyphset at cache %d\n", best);
376 FreeEntry(best);
377 glyphsetCache[best].count = 1;
378 if(prev_best >= 0) {
379 glyphsetCache[prev_best].next = glyphsetCache[best].next;
380 glyphsetCache[best].next = mru;
381 mru = best;
382 } else {
383 assert(mru == best);
385 return mru;
388 TRACE("Growing cache\n");
390 if (glyphsetCache)
391 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
392 glyphsetCache,
393 (glyphsetCacheSize + INIT_CACHE_SIZE)
394 * sizeof(*glyphsetCache));
395 else
396 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
397 (glyphsetCacheSize + INIT_CACHE_SIZE)
398 * sizeof(*glyphsetCache));
400 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
401 i++) {
402 glyphsetCache[i].next = i + 1;
403 glyphsetCache[i].count = -1;
405 glyphsetCache[i-1].next = -1;
406 glyphsetCacheSize += INIT_CACHE_SIZE;
408 lastfree = glyphsetCache[best].next;
409 glyphsetCache[best].count = 1;
410 glyphsetCache[best].next = mru;
411 mru = best;
412 TRACE("new free cache slot at %d\n", mru);
413 return mru;
416 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
418 DWORD size;
419 WORD *gasp, *buffer;
420 WORD num_recs;
421 DWORD ppem;
422 TEXTMETRICW tm;
424 *flags = 0;
426 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
427 if(size == GDI_ERROR)
428 return FALSE;
430 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
431 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
433 GetTextMetricsW(physDev->hdc, &tm);
434 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
436 gasp++;
437 num_recs = get_be_word(*gasp);
438 gasp++;
439 while(num_recs--)
441 *flags = get_be_word(*(gasp + 1));
442 if(ppem <= get_be_word(*gasp))
443 break;
444 gasp += 2;
446 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
448 HeapFree(GetProcessHeap(), 0, buffer);
449 return TRUE;
452 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
454 int ret;
455 int format;
456 gsCacheEntry *entry;
457 WORD flags;
458 static int hinter = -1;
460 if((ret = LookupEntry(plfsz)) != -1) return ret;
462 ret = AllocEntry();
463 entry = glyphsetCache + ret;
464 entry->lfsz = *plfsz;
465 for( format = 0; format < AA_MAXVALUE; format++ ) {
466 assert( !entry->format[format] );
469 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
471 if(hinter == -1)
473 RASTERIZER_STATUS status;
474 GetRasterizerCaps(&status, sizeof(status));
475 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
477 if(!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
478 entry->aa_default = AA_Grey;
479 else
480 entry->aa_default = AA_None;
482 else
483 entry->aa_default = AA_None;
485 return ret;
488 static void dec_ref_cache(int index)
490 assert(index >= 0);
491 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
492 assert(glyphsetCache[index].count > 0);
493 glyphsetCache[index].count--;
496 static void lfsz_calc_hash(LFANDSIZE *plfsz)
498 DWORD hash = 0, *ptr;
499 int i;
501 hash ^= plfsz->devsize.cx;
502 hash ^= plfsz->devsize.cy;
503 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
504 hash ^= *ptr;
505 for(i = 0, ptr = (DWORD*)&plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
506 WCHAR *pwc = (WCHAR *)ptr;
507 if(!*pwc) break;
508 hash ^= *ptr;
509 pwc++;
510 if(!*pwc) break;
512 plfsz->hash = hash;
513 return;
516 /***********************************************************************
517 * X11DRV_XRender_Finalize
519 void X11DRV_XRender_Finalize(void)
521 int i;
523 EnterCriticalSection(&xrender_cs);
524 for(i = mru; i >= 0; i = glyphsetCache[i].next)
525 FreeEntry(i);
526 LeaveCriticalSection(&xrender_cs);
530 /***********************************************************************
531 * X11DRV_XRender_SelectFont
533 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
535 LFANDSIZE lfsz;
537 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
538 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
539 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
540 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
541 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
542 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
543 lfsz_calc_hash(&lfsz);
545 EnterCriticalSection(&xrender_cs);
546 if(!physDev->xrender) {
547 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
548 sizeof(*physDev->xrender));
549 physDev->xrender->cache_index = -1;
551 else if(physDev->xrender->cache_index != -1)
552 dec_ref_cache(physDev->xrender->cache_index);
553 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
554 LeaveCriticalSection(&xrender_cs);
555 return 0;
558 /***********************************************************************
559 * X11DRV_XRender_DeleteDC
561 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
563 X11DRV_XRender_UpdateDrawable(physDev);
565 EnterCriticalSection(&xrender_cs);
566 if(physDev->xrender->cache_index != -1)
567 dec_ref_cache(physDev->xrender->cache_index);
568 LeaveCriticalSection(&xrender_cs);
570 HeapFree(GetProcessHeap(), 0, physDev->xrender);
571 physDev->xrender = NULL;
572 return;
575 /***********************************************************************
576 * X11DRV_XRender_UpdateDrawable
578 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
579 * It deletes the pict and tile when the drawable changes.
581 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
583 wine_tsx11_lock();
585 if(physDev->xrender->pict)
587 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
588 XFlush(gdi_display);
589 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
590 physDev->xrender->pict = 0;
592 wine_tsx11_unlock();
594 return;
597 /************************************************************************
598 * UploadGlyph
600 * Helper to ExtTextOut. Must be called inside xrender_cs
602 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
604 unsigned int buflen;
605 char *buf;
606 Glyph gid;
607 GLYPHMETRICS gm;
608 XGlyphInfo gi;
609 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
610 gsCacheEntryFormat *formatEntry;
611 UINT ggo_format = GGO_GLYPH_INDEX;
612 XRenderPictFormat pf;
613 static const char zero[4];
615 switch(format) {
616 case AA_Grey:
617 ggo_format |= WINE_GGO_GRAY16_BITMAP;
618 break;
620 default:
621 ERR("aa = %d - not implemented\n", format);
622 case AA_None:
623 ggo_format |= GGO_BITMAP;
624 break;
627 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
628 NULL);
629 if(buflen == GDI_ERROR) {
630 if(format != AA_None) {
631 format = AA_None;
632 entry->aa_default = AA_None;
633 ggo_format &= ~WINE_GGO_GRAY16_BITMAP;
634 ggo_format |= GGO_BITMAP;
635 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
636 NULL);
638 if(buflen == GDI_ERROR) {
639 WARN("GetGlyphOutlineW failed\n");
640 return FALSE;
642 TRACE("Turning off antialiasing for this monochrome font\n");
645 /* If there is nothing for the current type, we create the entry. */
646 if( !entry->format[format] ) {
647 entry->format[format] = HeapAlloc(GetProcessHeap(),
648 HEAP_ZERO_MEMORY,
649 sizeof(gsCacheEntryFormat));
651 formatEntry = entry->format[format];
653 if(formatEntry->nrealized <= glyph) {
654 formatEntry->nrealized = (glyph / 128 + 1) * 128;
656 if (formatEntry->realized)
657 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
658 HEAP_ZERO_MEMORY,
659 formatEntry->realized,
660 formatEntry->nrealized * sizeof(BOOL));
661 else
662 formatEntry->realized = HeapAlloc(GetProcessHeap(),
663 HEAP_ZERO_MEMORY,
664 formatEntry->nrealized * sizeof(BOOL));
666 if(!X11DRV_XRender_Installed) {
667 if (formatEntry->bitmaps)
668 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
669 HEAP_ZERO_MEMORY,
670 formatEntry->bitmaps,
671 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
672 else
673 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
674 HEAP_ZERO_MEMORY,
675 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
677 if (formatEntry->gis)
678 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
679 HEAP_ZERO_MEMORY,
680 formatEntry->gis,
681 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
682 else
683 formatEntry->gis = HeapAlloc(GetProcessHeap(),
684 HEAP_ZERO_MEMORY,
685 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
689 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
690 switch(format) {
691 case AA_Grey:
692 pf.depth = 8;
693 pf.direct.alphaMask = 0xff;
694 break;
696 default:
697 ERR("aa = %d - not implemented\n", format);
698 case AA_None:
699 pf.depth = 1;
700 pf.direct.alphaMask = 1;
701 break;
704 pf.type = PictTypeDirect;
705 pf.direct.alpha = 0;
707 wine_tsx11_lock();
708 formatEntry->font_format = pXRenderFindFormat(gdi_display,
709 PictFormatType |
710 PictFormatDepth |
711 PictFormatAlpha |
712 PictFormatAlphaMask,
713 &pf, 0);
715 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
716 wine_tsx11_unlock();
720 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
721 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, NULL);
722 formatEntry->realized[glyph] = TRUE;
724 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
725 buflen,
726 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
727 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
729 gi.width = gm.gmBlackBoxX;
730 gi.height = gm.gmBlackBoxY;
731 gi.x = -gm.gmptGlyphOrigin.x;
732 gi.y = gm.gmptGlyphOrigin.y;
733 gi.xOff = gm.gmCellIncX;
734 gi.yOff = gm.gmCellIncY;
736 if(TRACE_ON(xrender)) {
737 int pitch, i, j;
738 char output[300];
739 unsigned char *line;
741 if(format == AA_None) {
742 pitch = ((gi.width + 31) / 32) * 4;
743 for(i = 0; i < gi.height; i++) {
744 line = (unsigned char*) buf + i * pitch;
745 output[0] = '\0';
746 for(j = 0; j < pitch * 8; j++) {
747 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
749 strcat(output, "\n");
750 TRACE(output);
752 } else {
753 static const char blks[] = " .:;!o*#";
754 char str[2];
756 str[1] = '\0';
757 pitch = ((gi.width + 3) / 4) * 4;
758 for(i = 0; i < gi.height; i++) {
759 line = (unsigned char*) buf + i * pitch;
760 output[0] = '\0';
761 for(j = 0; j < pitch; j++) {
762 str[0] = blks[line[j] >> 5];
763 strcat(output, str);
765 strcat(output, "\n");
766 TRACE(output);
772 if(formatEntry->glyphset) {
773 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
774 unsigned char *byte = (unsigned char*) buf, c;
775 int i = buflen;
777 while(i--) {
778 c = *byte;
780 /* magic to flip bit order */
781 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
782 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
783 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
785 *byte++ = c;
788 gid = glyph;
791 XRenderCompositeText seems to ignore 0x0 glyphs when
792 AA_None, which means we lose the advance width of glyphs
793 like the space. We'll pretend that such glyphs are 1x1
794 bitmaps.
797 if(buflen == 0)
798 gi.width = gi.height = 1;
800 wine_tsx11_lock();
801 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
802 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
803 wine_tsx11_unlock();
804 HeapFree(GetProcessHeap(), 0, buf);
805 } else {
806 formatEntry->bitmaps[glyph] = buf;
809 memcpy(&formatEntry->gis[glyph], &gi, sizeof(gi));
811 return TRUE;
814 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
815 void *bitmap, XGlyphInfo *gi)
817 unsigned char *srcLine = bitmap, *src;
818 unsigned char bits, bitsMask;
819 int width = gi->width;
820 int stride = ((width + 31) & ~31) >> 3;
821 int height = gi->height;
822 int w;
823 int xspan, lenspan;
825 TRACE("%d, %d\n", x, y);
826 x -= gi->x;
827 y -= gi->y;
828 while (height--)
830 src = srcLine;
831 srcLine += stride;
832 w = width;
834 bitsMask = 0x80; /* FreeType is always MSB first */
835 bits = *src++;
837 xspan = x;
838 while (w)
840 if (bits & bitsMask)
842 lenspan = 0;
845 lenspan++;
846 if (lenspan == w)
847 break;
848 bitsMask = bitsMask >> 1;
849 if (!bitsMask)
851 bits = *src++;
852 bitsMask = 0x80;
854 } while (bits & bitsMask);
855 XFillRectangle (gdi_display, physDev->drawable,
856 physDev->gc, xspan, y, lenspan, 1);
857 xspan += lenspan;
858 w -= lenspan;
860 else
864 w--;
865 xspan++;
866 if (!w)
867 break;
868 bitsMask = bitsMask >> 1;
869 if (!bitsMask)
871 bits = *src++;
872 bitsMask = 0x80;
874 } while (!(bits & bitsMask));
877 y++;
881 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
882 void *bitmap, XGlyphInfo *gi)
884 unsigned char *srcLine = bitmap, *src, bits;
885 int width = gi->width;
886 int stride = ((width + 3) & ~3);
887 int height = gi->height;
888 int w;
889 int xspan, lenspan;
891 x -= gi->x;
892 y -= gi->y;
893 while (height--)
895 src = srcLine;
896 srcLine += stride;
897 w = width;
899 bits = *src++;
900 xspan = x;
901 while (w)
903 if (bits >= 0x80)
905 lenspan = 0;
908 lenspan++;
909 if (lenspan == w)
910 break;
911 bits = *src++;
912 } while (bits >= 0x80);
913 XFillRectangle (gdi_display, physDev->drawable,
914 physDev->gc, xspan, y, lenspan, 1);
915 xspan += lenspan;
916 w -= lenspan;
918 else
922 w--;
923 xspan++;
924 if (!w)
925 break;
926 bits = *src++;
927 } while (bits < 0x80);
930 y++;
935 static void ExamineBitfield (DWORD mask, int *shift, int *len)
937 int s, l;
939 s = 0;
940 while ((mask & 1) == 0)
942 mask >>= 1;
943 s++;
945 l = 0;
946 while ((mask & 1) == 1)
948 mask >>= 1;
949 l++;
951 *shift = s;
952 *len = l;
955 static DWORD GetField (DWORD pixel, int shift, int len)
957 pixel = pixel & (((1 << (len)) - 1) << shift);
958 pixel = pixel << (32 - (shift + len)) >> 24;
959 while (len < 8)
961 pixel |= (pixel >> len);
962 len <<= 1;
964 return pixel;
968 static DWORD PutField (DWORD pixel, int shift, int len)
970 shift = shift - (8 - len);
971 if (len <= 8)
972 pixel &= (((1 << len) - 1) << (8 - len));
973 if (shift < 0)
974 pixel >>= -shift;
975 else
976 pixel <<= shift;
977 return pixel;
980 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
981 int color)
983 int r_shift, r_len;
984 int g_shift, g_len;
985 int b_shift, b_len;
986 BYTE *maskLine, *mask, m;
987 int maskStride;
988 DWORD pixel;
989 int width, height;
990 int w, tx;
991 BYTE src_r, src_g, src_b;
993 x -= gi->x;
994 y -= gi->y;
995 width = gi->width;
996 height = gi->height;
998 maskLine = (unsigned char *) bitmap;
999 maskStride = (width + 3) & ~3;
1001 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1002 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1003 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1005 src_r = GetField(color, r_shift, r_len);
1006 src_g = GetField(color, g_shift, g_len);
1007 src_b = GetField(color, b_shift, b_len);
1009 for(; height--; y++)
1011 mask = maskLine;
1012 maskLine += maskStride;
1013 w = width;
1014 tx = x;
1016 if(y < 0) continue;
1017 if(y >= image->height) break;
1019 for(; w--; tx++)
1021 if(tx >= image->width) break;
1023 m = *mask++;
1024 if(tx < 0) continue;
1026 if (m == 0xff)
1027 XPutPixel (image, tx, y, color);
1028 else if (m)
1030 BYTE r, g, b;
1032 pixel = XGetPixel (image, tx, y);
1034 r = GetField(pixel, r_shift, r_len);
1035 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1036 g = GetField(pixel, g_shift, g_len);
1037 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1038 b = GetField(pixel, b_shift, b_len);
1039 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1041 pixel = (PutField (r, r_shift, r_len) |
1042 PutField (g, g_shift, g_len) |
1043 PutField (b, b_shift, b_len));
1044 XPutPixel (image, tx, y, pixel);
1050 /*************************************************************
1051 * get_tile_pict
1053 * Returns an appropiate Picture for tiling the text colour.
1054 * Call and use result within the xrender_cs
1056 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1058 static struct
1060 Pixmap xpm;
1061 Picture pict;
1062 int current_color;
1063 } tiles[2], *tile;
1064 XRenderColor col;
1066 tile = &tiles[type];
1068 if(!tile->xpm)
1070 XRenderPictureAttributes pa;
1072 wine_tsx11_lock();
1073 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1075 pa.repeat = True;
1076 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1077 wine_tsx11_unlock();
1079 /* init current_color to something different from text_pixel */
1080 tile->current_color = ~text_pixel;
1082 if(type == mono_drawable)
1084 /* for a 1bpp bitmap we always need a 1 in the tile */
1085 col.red = col.green = col.blue = 0;
1086 col.alpha = 0xffff;
1087 wine_tsx11_lock();
1088 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1089 wine_tsx11_unlock();
1093 if(text_pixel != tile->current_color && type == color_drawable)
1095 /* Map 0 -- 0xff onto 0 -- 0xffff */
1096 int r_shift, r_len;
1097 int g_shift, g_len;
1098 int b_shift, b_len;
1100 ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1101 ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1102 ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1104 col.red = GetField(text_pixel, r_shift, r_len);
1105 col.red |= col.red << 8;
1106 col.green = GetField(text_pixel, g_shift, g_len);
1107 col.green |= col.green << 8;
1108 col.blue = GetField(text_pixel, b_shift, b_len);
1109 col.blue |= col.blue << 8;
1110 col.alpha = 0x0;
1112 wine_tsx11_lock();
1113 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1114 wine_tsx11_unlock();
1115 tile->current_color = text_pixel;
1117 return tile->pict;
1120 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1122 return 1;
1125 /***********************************************************************
1126 * X11DRV_XRender_ExtTextOut
1128 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1129 const RECT *lprect, LPCWSTR wstr, UINT count,
1130 const INT *lpDx )
1132 RGNDATA *data;
1133 XGCValues xgcval;
1134 gsCacheEntry *entry;
1135 gsCacheEntryFormat *formatEntry;
1136 BOOL retv = FALSE;
1137 HDC hdc = physDev->hdc;
1138 int textPixel, backgroundPixel;
1139 HRGN saved_region = 0;
1140 BOOL disable_antialias = FALSE;
1141 AA_Type aa_type = AA_None;
1142 DIBSECTION bmp;
1143 unsigned int idx;
1144 double cosEsc, sinEsc;
1145 LOGFONTW lf;
1146 enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1147 Picture tile_pict = 0;
1149 /* Do we need to disable antialiasing because of palette mode? */
1150 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1151 TRACE("bitmap is not a DIB\n");
1153 else if (bmp.dsBmih.biBitCount <= 8) {
1154 TRACE("Disabling antialiasing\n");
1155 disable_antialias = TRUE;
1158 xgcval.function = GXcopy;
1159 xgcval.background = physDev->backgroundPixel;
1160 xgcval.fill_style = FillSolid;
1161 wine_tsx11_lock();
1162 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1163 wine_tsx11_unlock();
1165 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1167 if(physDev->depth == 1) {
1168 if((physDev->textPixel & 0xffffff) == 0) {
1169 textPixel = 0;
1170 backgroundPixel = 1;
1171 } else {
1172 textPixel = 1;
1173 backgroundPixel = 0;
1175 } else {
1176 textPixel = physDev->textPixel;
1177 backgroundPixel = physDev->backgroundPixel;
1180 if(flags & ETO_OPAQUE)
1182 wine_tsx11_lock();
1183 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1184 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1185 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1186 lprect->right - lprect->left, lprect->bottom - lprect->top );
1187 wine_tsx11_unlock();
1190 if(count == 0)
1192 retv = TRUE;
1193 goto done_unlock;
1197 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1198 if(lf.lfEscapement != 0) {
1199 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1200 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1201 } else {
1202 cosEsc = 1;
1203 sinEsc = 0;
1206 if (flags & ETO_CLIPPED)
1208 HRGN clip_region;
1210 clip_region = CreateRectRgnIndirect( lprect );
1211 /* make a copy of the current device region */
1212 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1213 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1214 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1215 DeleteObject( clip_region );
1218 if(X11DRV_XRender_Installed) {
1219 if(!physDev->xrender->pict) {
1220 XRenderPictureAttributes pa;
1221 pa.subwindow_mode = IncludeInferiors;
1223 wine_tsx11_lock();
1224 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1225 physDev->drawable,
1226 pict_formats[depth_type],
1227 CPSubwindowMode, &pa);
1228 wine_tsx11_unlock();
1230 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1231 physDev->xrender->pict, hdc, physDev->drawable);
1232 } else {
1233 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1234 physDev->xrender->pict, hdc, physDev->drawable);
1237 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1239 wine_tsx11_lock();
1240 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1241 physDev->dc_rect.left, physDev->dc_rect.top,
1242 (XRectangle *)data->Buffer, data->rdh.nCount );
1243 wine_tsx11_unlock();
1244 HeapFree( GetProcessHeap(), 0, data );
1248 EnterCriticalSection(&xrender_cs);
1250 entry = glyphsetCache + physDev->xrender->cache_index;
1251 if( disable_antialias == FALSE )
1252 aa_type = entry->aa_default;
1253 formatEntry = entry->format[aa_type];
1255 for(idx = 0; idx < count; idx++) {
1256 if( !formatEntry ) {
1257 UploadGlyph(physDev, wstr[idx], aa_type);
1258 /* re-evaluate antialias since aa_default may have changed */
1259 if( disable_antialias == FALSE )
1260 aa_type = entry->aa_default;
1261 formatEntry = entry->format[aa_type];
1262 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1263 UploadGlyph(physDev, wstr[idx], aa_type);
1266 if (!formatEntry)
1268 WARN("could not upload requested glyphs\n");
1269 LeaveCriticalSection(&xrender_cs);
1270 goto done_unlock;
1273 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1274 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1276 if(X11DRV_XRender_Installed)
1278 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1279 INT offset = 0;
1280 POINT desired, current;
1281 int render_op = PictOpOver;
1283 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1284 So we pass zeros to the function and move to our starting position using the first
1285 element of the elts array. */
1287 desired.x = physDev->dc_rect.left + x;
1288 desired.y = physDev->dc_rect.top + y;
1289 current.x = current.y = 0;
1291 tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1293 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1295 if((depth_type == mono_drawable) && (textPixel == 0))
1296 render_op = PictOpOutReverse; /* This gives us 'black' text */
1298 for(idx = 0; idx < count; idx++)
1300 elts[idx].glyphset = formatEntry->glyphset;
1301 elts[idx].chars = wstr + idx;
1302 elts[idx].nchars = 1;
1303 elts[idx].xOff = desired.x - current.x;
1304 elts[idx].yOff = desired.y - current.y;
1306 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1307 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1309 if(!lpDx)
1311 desired.x += formatEntry->gis[wstr[idx]].xOff;
1312 desired.y += formatEntry->gis[wstr[idx]].yOff;
1314 else
1316 offset += lpDx[idx];
1317 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1318 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1321 wine_tsx11_lock();
1322 pXRenderCompositeText16(gdi_display, render_op,
1323 tile_pict,
1324 physDev->xrender->pict,
1325 formatEntry->font_format,
1326 0, 0, 0, 0, elts, count);
1327 wine_tsx11_unlock();
1328 HeapFree(GetProcessHeap(), 0, elts);
1329 } else {
1330 INT offset = 0, xoff = 0, yoff = 0;
1331 wine_tsx11_lock();
1332 XSetForeground( gdi_display, physDev->gc, textPixel );
1334 if(aa_type == AA_None || physDev->depth == 1)
1336 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1338 if(aa_type == AA_None)
1339 sharp_glyph_fn = SharpGlyphMono;
1340 else
1341 sharp_glyph_fn = SharpGlyphGray;
1343 for(idx = 0; idx < count; idx++) {
1344 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1345 physDev->dc_rect.top + y + yoff,
1346 formatEntry->bitmaps[wstr[idx]],
1347 &formatEntry->gis[wstr[idx]]);
1348 if(lpDx) {
1349 offset += lpDx[idx];
1350 xoff = offset * cosEsc;
1351 yoff = offset * -sinEsc;
1352 } else {
1353 xoff += formatEntry->gis[wstr[idx]].xOff;
1354 yoff += formatEntry->gis[wstr[idx]].yOff;
1357 } else {
1358 XImage *image;
1359 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1360 RECT extents = {0, 0, 0, 0};
1361 POINT cur = {0, 0};
1362 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1363 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1365 TRACE("drawable %dx%d\n", w, h);
1367 for(idx = 0; idx < count; idx++) {
1368 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1369 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1370 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1371 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1372 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1373 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1374 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1375 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1376 if(lpDx) {
1377 offset += lpDx[idx];
1378 cur.x = offset * cosEsc;
1379 cur.y = offset * -sinEsc;
1380 } else {
1381 cur.x += formatEntry->gis[wstr[idx]].xOff;
1382 cur.y += formatEntry->gis[wstr[idx]].yOff;
1385 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1386 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1388 if(physDev->dc_rect.left + x + extents.left >= 0) {
1389 image_x = physDev->dc_rect.left + x + extents.left;
1390 image_off_x = 0;
1391 } else {
1392 image_x = 0;
1393 image_off_x = physDev->dc_rect.left + x + extents.left;
1395 if(physDev->dc_rect.top + y + extents.top >= 0) {
1396 image_y = physDev->dc_rect.top + y + extents.top;
1397 image_off_y = 0;
1398 } else {
1399 image_y = 0;
1400 image_off_y = physDev->dc_rect.top + y + extents.top;
1402 if(physDev->dc_rect.left + x + extents.right < w)
1403 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1404 else
1405 image_w = w - image_x;
1406 if(physDev->dc_rect.top + y + extents.bottom < h)
1407 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1408 else
1409 image_h = h - image_y;
1411 if(image_w <= 0 || image_h <= 0) goto no_image;
1413 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1414 image = XGetImage(gdi_display, physDev->drawable,
1415 image_x, image_y, image_w, image_h,
1416 AllPlanes, ZPixmap);
1417 X11DRV_check_error();
1419 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1420 gdi_display, (int)physDev->drawable, image_x, image_y,
1421 image_w, image_h, AllPlanes, ZPixmap,
1422 physDev->depth, image);
1423 if(!image) {
1424 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1425 physDev->depth);
1426 GC gc;
1427 XGCValues gcv;
1429 gcv.graphics_exposures = False;
1430 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1431 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1432 image_w, image_h, 0, 0);
1433 XFreeGC(gdi_display, gc);
1434 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1435 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1436 ZPixmap);
1437 X11DRV_check_error();
1438 XFreePixmap(gdi_display, xpm);
1440 if(!image) goto no_image;
1442 image->red_mask = visual->red_mask;
1443 image->green_mask = visual->green_mask;
1444 image->blue_mask = visual->blue_mask;
1446 offset = xoff = yoff = 0;
1447 for(idx = 0; idx < count; idx++) {
1448 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1449 yoff + image_off_y - extents.top,
1450 formatEntry->bitmaps[wstr[idx]],
1451 &formatEntry->gis[wstr[idx]],
1452 physDev->textPixel);
1453 if(lpDx) {
1454 offset += lpDx[idx];
1455 xoff = offset * cosEsc;
1456 yoff = offset * -sinEsc;
1457 } else {
1458 xoff += formatEntry->gis[wstr[idx]].xOff;
1459 yoff += formatEntry->gis[wstr[idx]].yOff;
1462 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1463 image_x, image_y, image_w, image_h);
1464 XDestroyImage(image);
1466 no_image:
1467 wine_tsx11_unlock();
1469 LeaveCriticalSection(&xrender_cs);
1471 if (flags & ETO_CLIPPED)
1473 /* restore the device region */
1474 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1475 DeleteObject( saved_region );
1478 retv = TRUE;
1480 done_unlock:
1481 X11DRV_UnlockDIBSection( physDev, TRUE );
1482 return retv;
1485 /******************************************************************************
1486 * AlphaBlend (x11drv.@)
1488 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1489 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1490 BLENDFUNCTION blendfn)
1492 XRenderPictureAttributes pa;
1493 XRenderPictFormat *src_format;
1494 XRenderPictFormat argb32_templ = {
1495 0, /* id */
1496 PictTypeDirect, /* type */
1497 32, /* depth */
1498 { /* direct */
1499 16, /* direct.red */
1500 0xff, /* direct.redMask */
1501 8, /* direct.green */
1502 0xff, /* direct.greenMask */
1503 0, /* direct.blue */
1504 0xff, /* direct.blueMask */
1505 24, /* direct.alpha */
1506 0xff, /* direct.alphaMask */
1508 0, /* colormap */
1510 unsigned long argb32_templ_mask =
1511 PictFormatType |
1512 PictFormatDepth |
1513 PictFormatRed |
1514 PictFormatRedMask |
1515 PictFormatGreen |
1516 PictFormatGreenMask |
1517 PictFormatBlue |
1518 PictFormatBlueMask |
1519 PictFormatAlpha |
1520 PictFormatAlphaMask;
1522 Picture dst_pict, src_pict;
1523 Pixmap xpm;
1524 DIBSECTION dib;
1525 XImage *image;
1526 GC gc;
1527 XGCValues gcv;
1528 DWORD *dstbits, *data;
1529 int y, y2;
1530 POINT pts[2];
1531 BOOL top_down = FALSE;
1532 RGNDATA *rgndata;
1533 enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1535 if(!X11DRV_XRender_Installed) {
1536 FIXME("Unable to AlphaBlend without Xrender\n");
1537 return FALSE;
1539 pts[0].x = xDst;
1540 pts[0].y = yDst;
1541 pts[1].x = xDst + widthDst;
1542 pts[1].y = yDst + heightDst;
1543 LPtoDP(devDst->hdc, pts, 2);
1544 xDst = pts[0].x;
1545 yDst = pts[0].y;
1546 widthDst = pts[1].x - pts[0].x;
1547 heightDst = pts[1].y - pts[0].y;
1549 pts[0].x = xSrc;
1550 pts[0].y = ySrc;
1551 pts[1].x = xSrc + widthSrc;
1552 pts[1].y = ySrc + heightSrc;
1553 LPtoDP(devSrc->hdc, pts, 2);
1554 xSrc = pts[0].x;
1555 ySrc = pts[0].y;
1556 widthSrc = pts[1].x - pts[0].x;
1557 heightSrc = pts[1].y - pts[0].y;
1558 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1560 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1561 if(widthDst != widthSrc || heightDst != heightSrc)
1562 #else
1563 if(!pXRenderSetPictureTransform)
1564 #endif
1566 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1567 return FALSE;
1570 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1572 FIXME("not a dibsection\n");
1573 return FALSE;
1576 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1577 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1579 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1580 SetLastError(ERROR_INVALID_PARAMETER);
1581 return FALSE;
1584 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1585 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1587 if(dib.dsBm.bmBitsPixel != 32) {
1588 FIXME("not a 32 bpp dibsection\n");
1589 return FALSE;
1591 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1593 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1594 top_down = TRUE;
1595 dstbits += widthSrc * (heightSrc - 1);
1596 y2 = ySrc;
1597 y = y2 + heightSrc - 1;
1599 else
1601 y = dib.dsBmih.biHeight - ySrc - 1;
1602 y2 = y - heightSrc + 1;
1605 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1607 for(; y >= y2; y--)
1609 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1610 widthSrc * 4);
1611 dstbits += (top_down ? -1 : 1) * widthSrc;
1614 else
1616 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1617 int x;
1619 for(; y >= y2; y--)
1621 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1622 for (x = 0; x < widthSrc; x++)
1624 DWORD argb = *srcbits++;
1625 argb = (argb & 0xffffff) | source_alpha;
1626 *dstbits++ = argb;
1628 if (top_down) /* we traversed the row forward so we should go back by two rows */
1629 dstbits -= 2 * widthSrc;
1634 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1636 wine_tsx11_lock();
1637 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1638 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1641 Avoid using XRenderFindStandardFormat as older libraries don't have it
1642 src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1644 src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1646 TRACE("src_format %p\n", src_format);
1648 pa.subwindow_mode = IncludeInferiors;
1650 /* FIXME use devDst->xrender->pict ? */
1651 dst_pict = pXRenderCreatePicture(gdi_display,
1652 devDst->drawable,
1653 pict_formats[dst_depth_type],
1654 CPSubwindowMode, &pa);
1655 TRACE("dst_pict %08lx\n", dst_pict);
1656 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1657 xpm = XCreatePixmap(gdi_display,
1658 root_window,
1659 widthSrc, heightSrc, 32);
1660 gcv.graphics_exposures = False;
1661 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1662 TRACE("xpm = %08lx\n", xpm);
1663 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1665 src_pict = pXRenderCreatePicture(gdi_display,
1666 xpm, src_format,
1667 CPSubwindowMode, &pa);
1668 TRACE("src_pict %08lx\n", src_pict);
1670 if (rgndata)
1672 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1673 devDst->dc_rect.left, devDst->dc_rect.top,
1674 (XRectangle *)rgndata->Buffer,
1675 rgndata->rdh.nCount );
1676 HeapFree( GetProcessHeap(), 0, rgndata );
1679 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1680 if(widthDst != widthSrc || heightDst != heightSrc) {
1681 double xscale = widthSrc/(double)widthDst;
1682 double yscale = heightSrc/(double)heightDst;
1683 XTransform xform = {{
1684 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1685 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1686 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1688 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1690 #endif
1691 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1692 0, 0, 0, 0,
1693 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1696 pXRenderFreePicture(gdi_display, src_pict);
1697 XFreePixmap(gdi_display, xpm);
1698 XFreeGC(gdi_display, gc);
1699 pXRenderFreePicture(gdi_display, dst_pict);
1700 image->data = NULL;
1701 XDestroyImage(image);
1703 wine_tsx11_unlock();
1704 HeapFree(GetProcessHeap(), 0, data);
1705 return TRUE;
1708 #else /* SONAME_LIBXRENDER */
1710 void X11DRV_XRender_Init(void)
1712 TRACE("XRender support not compiled in.\n");
1713 return;
1716 void X11DRV_XRender_Finalize(void)
1720 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1722 assert(0);
1723 return FALSE;
1726 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1728 assert(0);
1729 return;
1732 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1733 const RECT *lprect, LPCWSTR wstr, UINT count,
1734 const INT *lpDx )
1736 assert(0);
1737 return FALSE;
1740 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1742 assert(0);
1743 return;
1746 /******************************************************************************
1747 * AlphaBlend (x11drv.@)
1749 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1750 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1751 BLENDFUNCTION blendfn)
1753 FIXME("not supported - XRENDER headers were missing at compile time\n");
1754 return FALSE;
1757 #endif /* SONAME_LIBXRENDER */