This requires the first part of the font stuff. Huw D M Davies <hdavies@codeweavers.com> Uses Xrender extension to allow client side font rendering. Adds nice things like anti-aliased text.
? dlls/x11drv/xrender.c Index: dlls/x11drv/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/x11drv/Makefile.in,v retrieving revision 1.15 diff -u -r1.15 Makefile.in --- dlls/x11drv/Makefile.in 2001/10/18 21:38:59 1.15 +++ dlls/x11drv/Makefile.in 2002/01/26 13:36:58 @@ -15,6 +15,7 @@ winpos.c \ x11ddraw.c \ x11drv_main.c \ + xrender.c \ xvidmode.c PROGRAMS = \ Index: dlls/x11drv/x11drv.spec =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11drv.spec,v retrieving revision 1.28 diff -u -r1.28 x11drv.spec --- dlls/x11drv/x11drv.spec 2002/01/04 18:27:44 1.28 +++ dlls/x11drv/x11drv.spec 2002/01/26 13:36:58 @@ -9,7 +9,7 @@ import ntdll.dll debug_channels (bitblt bitmap clipboard cursor dinput event font gdi graphics - key keyboard opengl palette text win x11drv) + key keyboard opengl palette text win x11drv xrender) # GDI driver Index: graphics/x11drv/clipping.c =================================================================== RCS file: /home/wine/wine/graphics/x11drv/clipping.c,v retrieving revision 1.17 diff -u -r1.17 clipping.c --- graphics/x11drv/clipping.c 2001/07/25 00:43:35 1.17 +++ graphics/x11drv/clipping.c 2002/01/26 13:36:58 @@ -99,6 +99,8 @@ dc->DCOrgY = org_y; physDev->drawable = drawable; TSXSetSubwindowMode( gdi_display, physDev->gc, mode ); + if(physDev->xrender) + X11DRV_XRender_UpdateDrawable(dc); GDI_ReleaseObj( hdc ); } } Index: graphics/x11drv/init.c =================================================================== RCS file: /home/wine/wine/graphics/x11drv/init.c,v retrieving revision 1.42 diff -u -r1.42 init.c --- graphics/x11drv/init.c 2002/01/04 18:27:44 1.42 +++ graphics/x11drv/init.c 2002/01/26 13:36:59 @@ -66,6 +66,9 @@ if (!X11DRV_BITMAP_Init()) return FALSE; + /* Initialize XRender */ + X11DRV_XRender_Init(); + /* Initialize fonts and text caps */ log_pixels_x = MulDiv( WidthOfScreen(screen), 254, WidthMMOfScreen(screen) * 10 ); @@ -157,6 +160,9 @@ BOOL X11DRV_DeleteDC( DC *dc ) { X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + + if(physDev->xrender) + X11DRV_XRender_DeleteDC(dc); wine_tsx11_lock(); XFreeGC( gdi_display, physDev->gc ); while (physDev->used_visuals-- > 0) Index: graphics/x11drv/text.c =================================================================== RCS file: /home/wine/wine/graphics/x11drv/text.c,v retrieving revision 1.27 diff -u -r1.27 text.c --- graphics/x11drv/text.c 2001/07/25 00:43:35 1.27 +++ graphics/x11drv/text.c 2002/01/26 13:36:59 @@ -46,11 +46,17 @@ BOOL dibUpdateFlag = FALSE; BOOL result = TRUE; + + if(dc->gdiFont) + return X11DRV_XRender_ExtTextOut(dc, x, y, flags, lprect, wstr, count, + lpDx); + + if (!X11DRV_SetupGCForText( dc )) return TRUE; pfo = XFONT_GetFontObject( physDev->font ); font = pfo->fs; - + if (pfo->lf.lfEscapement && pfo->lpX11Trans) rotated = TRUE; dfBreakChar = (char)pfo->fi->df.dfBreakChar; Index: graphics/x11drv/xfont.c =================================================================== RCS file: /home/wine/wine/graphics/x11drv/xfont.c,v retrieving revision 1.89 diff -u -r1.89 xfont.c --- graphics/x11drv/xfont.c 2001/11/06 00:46:36 1.89 +++ graphics/x11drv/xfont.c 2002/01/26 13:36:59 @@ -206,6 +206,10 @@ { "1", DEFAULT_CHARSET, 0, X11DRV_CPTABLE_UNICODE }, { NULL, DEFAULT_CHARSET, 0, X11DRV_CPTABLE_UNICODE }}; +static const SuffixCharset sufch_dec[] = { + { "dectech", SYMBOL_CHARSET, CP_SYMBOL, X11DRV_CPTABLE_SBCS }, + { NULL, 0, 0, X11DRV_CPTABLE_SBCS }}; + /* Each of these must be matched explicitly */ static const SuffixCharset sufch_any[] = { { "fontspecific", SYMBOL_CHARSET, CP_SYMBOL, X11DRV_CPTABLE_SBCS }, @@ -244,6 +248,7 @@ { "unicode", sufch_unicode, &__fETTable[19]}, { "iso10646", sufch_iso10646, &__fETTable[20]}, { "cp", sufch_windows, &__fETTable[21]}, + { "dec", sufch_dec, &__fETTable[22]}, /* NULL prefix matches anything so put it last */ { NULL, sufch_any, NULL }, }; @@ -2919,6 +2924,10 @@ RAW_ASCENT = TSXInternAtom(gdi_display, "RAW_ASCENT", TRUE); RAW_DESCENT = TSXInternAtom(gdi_display, "RAW_DESCENT", TRUE); + + if(X11DRV_XRender_Installed) + XTextCaps |= TC_VA_ABLE; + return XTextCaps; } @@ -3166,10 +3175,16 @@ LOGFONT16 lf; X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + TRACE("dc=%p, hfont=%04x\n", dc, hfont); + if (!GetObjectW( hfont, sizeof(logfont), &logfont )) return GDI_ERROR; + + TRACE("dc->gdiFont = %p\n", dc->gdiFont); - /* If we want to use a gdi font, we should check for XRender extension - and return FALSE here */ + if(dc->gdiFont && X11DRV_XRender_Installed) { + X11DRV_XRender_SelectFont(dc, hfont); + return FALSE; + } EnterCriticalSection( &crtsc_fonts_X11 ); --- /dev/null Sat Mar 24 04:37:44 2001 +++ dlls/x11drv/xrender.c Sat Jan 26 15:22:37 2002 @@ -0,0 +1,826 @@ +/* + * Functions to use the XRender extension + * + * Copyright 2001 Huw D M Davies for CodeWeavers + */ +#include "config.h" + +#include "winnt.h" +#include "x11drv.h" +#include "bitmap.h" +#include "debugtools.h" +#include "region.h" +#include <string.h> +#include <stdlib.h> +#include "wine/unicode.h" +#include <assert.h> + +BOOL X11DRV_XRender_Installed = FALSE; +DEFAULT_DEBUG_CHANNEL(xrender); + +#ifdef HAVE_LIBXRENDER + +#include "ts_xlib.h" +#include "ts_xrender.h" + +static XRenderPictFormat *screen_format; /* format of screen */ +static XRenderPictFormat *mono_format; /* format of mono bitmap */ + +typedef struct +{ + LOGFONTW lf; + XFORM xform; /* this is dum as we don't care about offsets */ + DWORD hash; +} LFANDSIZE; + +#define INITIAL_REALIZED_BUF_SIZE 128 + + +typedef struct +{ + LFANDSIZE lfsz; + GlyphSet glyphset; + XRenderPictFormat *font_format; + int nrealized; + BOOL *realized; + UINT count; + INT next; +} gsCacheEntry; + +struct tagXRENDERINFO +{ + gsCacheEntry *cacheEntry; + Picture pict; + Picture tile_pict; + Pixmap tile_xpm; + COLORREF lastTextColor; +}; + + +static gsCacheEntry *glyphsetCache = NULL; +static DWORD glyphsetCacheSize = 0; +static INT lastfree = -1; +static INT mru = -1; + +#define INIT_CACHE_SIZE 10 + +static int antialias = 1; + +/*********************************************************************** + * X11DRV_XRender_Init + * + * Let's see if our XServer has the extension available + * + */ +void X11DRV_XRender_Init(void) +{ + int error_base, event_base, i; + XRenderPictFormat pf; + + if(TSXRenderQueryExtension(gdi_display, &event_base, &error_base)) { + X11DRV_XRender_Installed = TRUE; + TRACE("Xrender is up and running error_base = %d\n", error_base); + screen_format = TSXRenderFindVisualFormat(gdi_display, visual); + pf.type = PictTypeDirect; + pf.depth = 1; + pf.direct.alpha = 0; + pf.direct.alphaMask = 1; + mono_format = TSXRenderFindFormat(gdi_display, PictFormatType | + PictFormatDepth | PictFormatAlpha | + PictFormatAlphaMask, &pf, 0); + + glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(*glyphsetCache) * INIT_CACHE_SIZE); + + glyphsetCacheSize = INIT_CACHE_SIZE; + lastfree = 0; + for(i = 0; i < INIT_CACHE_SIZE; i++) { + glyphsetCache[i].next = i + 1; + glyphsetCache[i].count = -1; + } + glyphsetCache[i-1].next = -1; + } else { + TRACE("Xrender is not available on this server\n"); + } + return; +} + +static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2) +{ + if(p1->hash != p2->hash) return TRUE; + if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE; + if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE; + return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName); +} + +static void walk_cache(void) +{ + int i; + + for(i=mru; i >= 0; i = glyphsetCache[i].next) + TRACE("item %d\n", i); +} + +static gsCacheEntry *LookupEntry(LFANDSIZE *plfsz) +{ + int i, prev_i = -1; + + for(i = mru; i >= 0; i = glyphsetCache[i].next) { + TRACE("%d\n", i); + if(glyphsetCache[i].count == -1) { /* reached free list so stop */ + i = -1; + break; + } + + if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) { + glyphsetCache[i].count++; + if(prev_i >= 0) { + glyphsetCache[prev_i].next = glyphsetCache[i].next; + glyphsetCache[i].next = mru; + mru = i; + } + TRACE("found font in cache %d\n", i); + return glyphsetCache + i; + } + prev_i = i; + } + TRACE("font not in cache\n"); + return NULL; +} + +static gsCacheEntry *AllocEntry(void) +{ + int best = -1, prev_best = -1, i, prev_i = -1; + + if(lastfree >= 0) { + assert(glyphsetCache[lastfree].count == -1); + glyphsetCache[lastfree].count = 1; + best = lastfree; + lastfree = glyphsetCache[lastfree].next; + assert(best != mru); + glyphsetCache[best].next = mru; + mru = best; + + TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree); + return glyphsetCache + mru; + } + + for(i = mru; i >= 0; i = glyphsetCache[i].next) { + if(glyphsetCache[i].count == 0) { + best = i; + prev_best = prev_i; + } + prev_i = i; + } + + if(best >= 0) { + TRACE("freeing unused glyphset at cache %d\n", best); + TSXRenderFreeGlyphSet(gdi_display, glyphsetCache[best].glyphset); + glyphsetCache[best].glyphset = 0; + if(glyphsetCache[best].nrealized) { /* do we really want to do this? */ + HeapFree(GetProcessHeap(), 0, glyphsetCache[best].realized); + glyphsetCache[best].realized = NULL; + glyphsetCache[best].nrealized = 0; + } + glyphsetCache[best].count = 1; + if(prev_best >= 0) { + glyphsetCache[prev_best].next = glyphsetCache[best].next; + glyphsetCache[best].next = mru; + mru = best; + } else { + assert(mru == best); + } + return glyphsetCache + mru; + } + + TRACE("Growing cache\n"); + glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + glyphsetCache, + (glyphsetCacheSize + INIT_CACHE_SIZE) + * sizeof(*glyphsetCache)); + for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE; + i++) { + glyphsetCache[i].next = i + 1; + glyphsetCache[i].count = -1; + } + glyphsetCache[i-1].next = -1; + glyphsetCacheSize += INIT_CACHE_SIZE; + + lastfree = glyphsetCache[best].next; + glyphsetCache[best].count = 1; + glyphsetCache[best].next = mru; + mru = best; + TRACE("new free cache slot at %d\n", mru); + return glyphsetCache + mru; +} + +static gsCacheEntry *GetCacheEntry(LFANDSIZE *plfsz) +{ + XRenderPictFormat pf; + gsCacheEntry *ret; + + if((ret = LookupEntry(plfsz)) != NULL) return ret; + + ret = AllocEntry(); + ret->lfsz = *plfsz; + assert(ret->nrealized == 0); + + + if(antialias && abs(plfsz->lf.lfHeight) > 16) { + pf.depth = 8; + pf.direct.alphaMask = 0xff; + } else { + pf.depth = 1; + pf.direct.alphaMask = 1; + } + pf.type = PictTypeDirect; + pf.direct.alpha = 0; + + ret->font_format = TSXRenderFindFormat(gdi_display, + PictFormatType | + PictFormatDepth | + PictFormatAlpha | + PictFormatAlphaMask, + &pf, 0); + + ret->glyphset = TSXRenderCreateGlyphSet(gdi_display, ret->font_format); + return ret; +} + +static void dec_ref_cache(gsCacheEntry *entry) +{ + TRACE("dec'ing entry %d to %d\n", entry - glyphsetCache, entry->count - 1); + assert(entry->count > 0); + entry->count--; +} + +static void lfsz_calc_hash(LFANDSIZE *plfsz) +{ + DWORD hash = 0, *ptr; + int i; + + for(ptr = (DWORD*)&plfsz->xform; ptr < (DWORD*)(&plfsz->xform + 1); ptr++) + hash ^= *ptr; + for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++) + hash ^= *ptr; + for(i = 0, ptr = (DWORD*)&plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) { + WCHAR *pwc = (WCHAR *)ptr; + if(!*pwc) break; + hash ^= *ptr; + pwc++; + if(!*pwc) break; + } + plfsz->hash = hash; + return; +} + +/*********************************************************************** + * X11DRV_XRender_Finalize + */ +void X11DRV_XRender_Finalize(void) +{ + FIXME("Free cached glyphsets\n"); + return; +} + + +/*********************************************************************** + * X11DRV_XRender_SelectFont + */ +BOOL X11DRV_XRender_SelectFont(DC *dc, HFONT hfont) +{ + X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + LFANDSIZE lfsz; + + GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf); + TRACE("h=%ld w=%ld weight=%ld it=%d charset=%d name=%s\n", + lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight, + lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName)); + lfsz.xform = dc->xformWorld2Vport; + lfsz_calc_hash(&lfsz); + + if(!physDev->xrender) + physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(*physDev->xrender)); + + else if(physDev->xrender->cacheEntry) + dec_ref_cache(physDev->xrender->cacheEntry); + physDev->xrender->cacheEntry = GetCacheEntry(&lfsz); + return 0; +} + +/*********************************************************************** + * X11DRV_XRender_DeleteDC + */ +void X11DRV_XRender_DeleteDC(DC *dc) +{ + X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + + if(physDev->xrender->tile_pict) + TSXRenderFreePicture(gdi_display, physDev->xrender->tile_pict); + + if(physDev->xrender->tile_xpm) + TSXFreePixmap(gdi_display, physDev->xrender->tile_xpm); + + if(physDev->xrender->pict) { + TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, dc); + TSXRenderFreePicture(gdi_display, physDev->xrender->pict); + } + + if(physDev->xrender->cacheEntry) + dec_ref_cache(physDev->xrender->cacheEntry); + + HeapFree(GetProcessHeap(), 0, physDev->xrender); + physDev->xrender = NULL; + return; +} + +/*********************************************************************** + * X11DRV_XRender_UpdateDrawable + * + * This gets called from X11DRV_SetDrawable and deletes the pict when the + * drawable changes. However at the moment we delete the pict at the end of + * every ExtTextOut so this is basically a NOP. + */ +void X11DRV_XRender_UpdateDrawable(DC *dc) +{ + X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + + if(physDev->xrender->pict) { + TRACE("freeing pict %08lx from dc %p\n", physDev->xrender->pict, dc); + TSXRenderFreePicture(gdi_display, physDev->xrender->pict); + } + physDev->xrender->pict = 0; + return; +} + +static BOOL UploadGlyph(DC *dc, WCHAR glyph) +{ + X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + int buflen; + char *buf; + Glyph gid; + GLYPHMETRICS gm; + XGlyphInfo gi; + gsCacheEntry *entry = physDev->xrender->cacheEntry; + UINT ggo_format; + BOOL aa; + + if(entry->nrealized <= glyph) { + entry->nrealized = (glyph / 128 + 1) * 128; + entry->realized = HeapReAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + entry->realized, + entry->nrealized * sizeof(BOOL)); + } + entry->realized[glyph] = TRUE; + + if(entry->font_format->depth == 8) { + aa = TRUE; + ggo_format = WINE_GGO_GRAY16_BITMAP; + } else { + aa = FALSE; + ggo_format = GGO_BITMAP; + } + + buflen = GetGlyphOutlineW(dc->hSelf, glyph, ggo_format, &gm, 0, NULL, + NULL); + if(buflen == GDI_ERROR) + return FALSE; + + buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen); + GetGlyphOutlineW(dc->hSelf, glyph, ggo_format, &gm, buflen, buf, NULL); + + TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%ld,%ld\n", + buflen, + gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY, + gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y); + + gi.width = gm.gmBlackBoxX; + gi.height = gm.gmBlackBoxY; + gi.x = -gm.gmptGlyphOrigin.x; + gi.y = gm.gmptGlyphOrigin.y; + gi.xOff = gm.gmCellIncX; + gi.yOff = gm.gmCellIncY; + + if(TRACE_ON(xrender)) { + int pitch, i, j; + char output[300]; + unsigned char *line; + + if(!aa) { + pitch = ((gi.width + 31) / 32) * 4; + for(i = 0; i < gi.height; i++) { + line = buf + i * pitch; + output[0] = '\0'; + for(j = 0; j < pitch * 8; j++) { + strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " "); + } + strcat(output, "\n"); + TRACE(output); + } + } else { + char blks[] = " .:;!o*#"; + char str[2]; + + str[1] = '\0'; + pitch = ((gi.width + 3) / 4) * 4; + for(i = 0; i < gi.height; i++) { + line = buf + i * pitch; + output[0] = '\0'; + for(j = 0; j < pitch; j++) { + str[0] = blks[line[j] >> 5]; + strcat(output, str); + } + strcat(output, "\n"); + TRACE(output); + } + } + } + + if(!aa && BitmapBitOrder(gdi_display) != MSBFirst) { + unsigned char *byte = buf, c; + int i = buflen; + + while(i--) { + c = *byte; + + /* magic to flip bit order */ + c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55); + c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33); + c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f); + + *byte++ = c; + } + } + gid = glyph; + TSXRenderAddGlyphs(gdi_display, entry->glyphset, &gid, &gi, 1, + buf, buflen); + HeapFree(GetProcessHeap(), 0, buf); + return TRUE; +} + +/*********************************************************************** + * X11DRV_XRender_ExtTextOut + */ +BOOL X11DRV_XRender_ExtTextOut( DC *dc, INT x, INT y, UINT flags, + const RECT *lprect, LPCWSTR wstr, UINT count, + const INT *lpDx ) +{ + XRenderColor col; + int idx; + TEXTMETRICW tm; + X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev; + RGNOBJ *obj; + XRectangle *pXrect; + SIZE sz; + RECT rc; + BOOL done_extents = FALSE; + INT width, xwidth, ywidth; + double cosEsc, sinEsc; + XGCValues xgcval; + LOGFONTW lf; + int render_op = PictOpOver; + + TRACE("%04x, %d, %d, %08x, %p, %s, %d, %p)\n", dc->hSelf, x, y, flags, + lprect, debugstr_wn(wstr, count), count, lpDx); + if(lprect) + TRACE("rect: %d,%d - %d,%d\n", lprect->left, lprect->top, lprect->right, + lprect->bottom); + TRACE("align = %x bkmode = %x mapmode = %x\n", dc->textAlign, + dc->backgroundMode, + dc->MapMode); + + if(dc->textAlign & TA_UPDATECP) { + x = dc->CursPosX; + y = dc->CursPosY; + } + + GetObjectW(GetCurrentObject(dc->hSelf, OBJ_FONT), sizeof(lf), &lf); + if(lf.lfEscapement != 0) { + cosEsc = cos(lf.lfEscapement * M_PI / 1800); + sinEsc = sin(lf.lfEscapement * M_PI / 1800); + } else { + cosEsc = 1; + sinEsc = 0; + } + + if(flags & (ETO_CLIPPED | ETO_OPAQUE)) { + if(!lprect) { + if(flags & ETO_CLIPPED) return FALSE; + GetTextExtentPointW(dc->hSelf, wstr, count, &sz); + done_extents = TRUE; + rc.left = x; + rc.top = y; + rc.right = x + sz.cx; + rc.bottom = y + sz.cy; + } else { + rc = *lprect; + } + + rc.left = INTERNAL_XWPTODP(dc, rc.left, rc.top); + rc.top = INTERNAL_YWPTODP(dc, rc.left, rc.top); + rc.right = INTERNAL_XWPTODP(dc, rc.right, rc.bottom); + rc.bottom = INTERNAL_YWPTODP(dc, rc.right, rc.bottom); + + if(rc.left > rc.right) {INT tmp = rc.left; rc.left = rc.right; rc.right = tmp;} + if(rc.top > rc.bottom) {INT tmp = rc.top; rc.top = rc.bottom; rc.bottom = tmp;} + } + + xgcval.function = GXcopy; + xgcval.background = physDev->backgroundPixel; + xgcval.fill_style = FillSolid; + TSXChangeGC( gdi_display, physDev->gc, + GCFunction | GCBackground | GCFillStyle, &xgcval ); + + X11DRV_LockDIBSection( dc, DIB_Status_GdiMod, FALSE ); + + if(flags & ETO_OPAQUE) { + TSXSetForeground( gdi_display, physDev->gc, physDev->backgroundPixel ); + TSXFillRectangle( gdi_display, physDev->drawable, physDev->gc, + dc->DCOrgX + rc.left, dc->DCOrgY + rc.top, + rc.right - rc.left, rc.bottom - rc.top ); + } + + if(count == 0) { + X11DRV_UnlockDIBSection( dc, TRUE ); + return TRUE; + } + + x = INTERNAL_XWPTODP( dc, x, y ); + y = INTERNAL_YWPTODP( dc, x, y ); + + TRACE("real x,y %d,%d\n", x, y); + + if(lpDx) { + width = 0; + for(idx = 0; idx < count; idx++) + width += lpDx[idx]; + } else { + if(!done_extents) { + GetTextExtentPointW(dc->hSelf, wstr, count, &sz); + done_extents = TRUE; + } + width = sz.cx; + } + width = INTERNAL_XWSTODS(dc, width); + xwidth = width * cosEsc; + ywidth = width * sinEsc; + + GetTextMetricsW(dc->hSelf, &tm); + + switch( dc->textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) ) { + case TA_LEFT: + if (dc->textAlign & TA_UPDATECP) { + dc->CursPosX = INTERNAL_XDPTOWP( dc, x + xwidth, y - ywidth ); + dc->CursPosY = INTERNAL_YDPTOWP( dc, x + xwidth, y - ywidth ); + } + break; + + case TA_CENTER: + x -= xwidth / 2; + y += ywidth / 2; + break; + + case TA_RIGHT: + x -= xwidth; + y += ywidth; + if (dc->textAlign & TA_UPDATECP) { + dc->CursPosX = INTERNAL_XDPTOWP( dc, x + xwidth, y - ywidth ); + dc->CursPosY = INTERNAL_YDPTOWP( dc, x + xwidth, y - ywidth ); + } + break; + } + + switch( dc->textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) ) { + case TA_TOP: + y += tm.tmAscent * cosEsc; + x += tm.tmAscent * sinEsc; + break; + + case TA_BOTTOM: + y -= tm.tmDescent * cosEsc; + x -= tm.tmDescent * sinEsc; + break; + + case TA_BASELINE: + break; + } + + if (flags & ETO_CLIPPED) + { + SaveVisRgn16( dc->hSelf ); + CLIPPING_IntersectVisRect( dc, rc.left, rc.top, rc.right, + rc.bottom, FALSE ); + } + + + + if(!physDev->xrender->pict) { + XRenderPictureAttributes pa; + pa.subwindow_mode = IncludeInferiors; + + physDev->xrender->pict = + TSXRenderCreatePicture(gdi_display, + physDev->drawable, + (dc->bitsPerPixel == 1) ? + mono_format : screen_format, + CPSubwindowMode, &pa); + + TRACE("allocing pict = %lx dc = %p drawable = %08lx\n", physDev->xrender->pict, dc, physDev->drawable); + } else { + TRACE("using existing pict = %lx dc = %p\n", physDev->xrender->pict, dc); + } + + obj = (RGNOBJ *) GDI_GetObjPtr(dc->hGCClipRgn, REGION_MAGIC); + if (!obj) + { + ERR("Rgn is 0. Please report this.\n"); + return FALSE; + } + + if (obj->rgn->numRects > 0) + { + XRectangle *pXr; + RECT *pRect = obj->rgn->rects; + RECT *pEndRect = obj->rgn->rects + obj->rgn->numRects; + + pXrect = HeapAlloc( GetProcessHeap(), 0, + sizeof(*pXrect) * obj->rgn->numRects ); + if(!pXrect) + { + WARN("Can't alloc buffer\n"); + GDI_ReleaseObj( dc->hGCClipRgn ); + return FALSE; + } + + for(pXr = pXrect; pRect < pEndRect; pRect++, pXr++) + { + pXr->x = pRect->left; + pXr->y = pRect->top; + pXr->width = pRect->right - pRect->left; + pXr->height = pRect->bottom - pRect->top; + TRACE("Adding clip rect %d,%d - %d,%d\n", pRect->left, pRect->top, + pRect->right, pRect->bottom); + } + } + else { + TRACE("no clip rgn\n"); + pXrect = NULL; + } + + TSXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict, + 0, 0, pXrect, obj->rgn->numRects ); + + if(pXrect) + HeapFree( GetProcessHeap(), 0, pXrect ); + + GDI_ReleaseObj( dc->hGCClipRgn ); + + if(dc->backgroundMode != TRANSPARENT) { + if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE))) { + if(!(flags & ETO_OPAQUE) || x < rc.left || x + width >= rc.right || + y - tm.tmAscent < rc.top || y + tm.tmDescent >= rc.bottom) { + TSXSetForeground( gdi_display, physDev->gc, physDev->backgroundPixel ); + TSXFillRectangle( gdi_display, physDev->drawable, physDev->gc, + dc->DCOrgX + x, dc->DCOrgY + y - tm.tmAscent, + width, tm.tmAscent + tm.tmDescent ); + } + } + } + + /* Create a 1x1 pixmap to tile over the font mask */ + if(!physDev->xrender->tile_xpm) { + XRenderPictureAttributes pa; + + XRenderPictFormat *format = (dc->bitsPerPixel == 1) ? mono_format : screen_format; + physDev->xrender->tile_xpm = TSXCreatePixmap(gdi_display, + physDev->drawable, + 1, 1, + format->depth); + pa.repeat = True; + physDev->xrender->tile_pict = TSXRenderCreatePicture(gdi_display, + physDev->xrender->tile_xpm, + format, + CPRepeat, &pa); + TRACE("Created pixmap of depth %d\n", format->depth); + /* init lastTextColor to something different from dc->textColor */ + physDev->xrender->lastTextColor = ~dc->textColor; + + } + + if(dc->textColor != physDev->xrender->lastTextColor) { + if(dc->bitsPerPixel != 1) { + /* Map 0 -- 0xff onto 0 -- 0xffff */ + col.red = GetRValue(dc->textColor); + col.red |= col.red << 8; + col.green = GetGValue(dc->textColor); + col.green |= col.green << 8; + col.blue = GetBValue(dc->textColor); + col.blue |= col.blue << 8; + col.alpha = 0x0; + } else { /* for a 1bpp bitmap we always need a 1 in the tile */ + col.red = col.green = col.blue = 0; + col.alpha = 0xffff; + } + TSXRenderFillRectangle(gdi_display, PictOpSrc, + physDev->xrender->tile_pict, + &col, 0, 0, 1, 1); + physDev->xrender->lastTextColor = dc->textColor; + } + + /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation. + */ + if((dc->bitsPerPixel == 1) && ((dc->textColor & 0xffffff) == 0)) + render_op = PictOpOutReverse; /* This gives us 'black' text */ + + for(idx = 0; idx < count; idx++) { + if(wstr[idx] >= physDev->xrender->cacheEntry->nrealized || + physDev->xrender->cacheEntry->realized[wstr[idx]] == FALSE) { + UploadGlyph(dc, wstr[idx]); + } + } + + + TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count), dc->DCOrgX + x, + dc->DCOrgY + y); + if(!lpDx) + TSXRenderCompositeString16(gdi_display, render_op, + physDev->xrender->tile_pict, + physDev->xrender->pict, + physDev->xrender->cacheEntry->font_format, + physDev->xrender->cacheEntry->glyphset, + 0, 0, dc->DCOrgX + x, dc->DCOrgY + y, wstr, + count); + + else { + INT offset = 0, xoff = 0, yoff = 0; + for(idx = 0; idx < count; idx++) { + TSXRenderCompositeString16(gdi_display, render_op, + physDev->xrender->tile_pict, + physDev->xrender->pict, + physDev->xrender->cacheEntry->font_format, + physDev->xrender->cacheEntry->glyphset, + 0, 0, dc->DCOrgX + x + xoff, + dc->DCOrgY + y + yoff, + wstr + idx, 1); + offset += INTERNAL_XWSTODS(dc, lpDx[idx]); + xoff = offset * cosEsc; + yoff = offset * sinEsc; + } + } + + if(physDev->xrender->pict) { + TSXRenderFreePicture(gdi_display, physDev->xrender->pict); + } + physDev->xrender->pict = 0; + + + if (flags & ETO_CLIPPED) + RestoreVisRgn16( dc->hSelf ); + + X11DRV_UnlockDIBSection( dc, TRUE ); + return TRUE; +} + +#else /* #ifdef HAVE_LIBXRENDER */ + +void X11DRV_XRender_Init(void) +{ + TRACE("XRender support not compiled in.\n"); + return; +} + +void X11DRV_XRender_Finalize(void) +{ + assert(0); + return; +} + +BOOL X11DRV_XRender_SelectFont(DC *dc, HFONT hfont) +{ + assert(0); + return FALSE; +} + +void X11DRV_XRender_DeleteDC(DC *dc) +{ + assert(0); + return; +} + +BOOL X11DRV_XRender_ExtTextOut( DC *dc, INT x, INT y, UINT flags, + const RECT *lprect, LPCWSTR wstr, UINT count, + const INT *lpDx ) +{ + assert(0); + return FALSE; +} + +void X11DRV_XRender_UpdateDrawable(DC *dc) +{ + assert(0); + return; +} + +#endif