xrender.c 51.7 KB
Newer Older
1 2 3
/*
 * Functions to use the XRender extension
 *
4 5 6 7
 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
 *
 * Some parts also:
 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
22 23
 */
#include "config.h"
24 25 26
#include "wine/port.h"

#include <assert.h>
27
#include <stdarg.h>
28 29
#include <string.h>
#include <stdlib.h>
30

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"
38

39 40
int using_client_side_fonts = FALSE;

41
WINE_DEFAULT_DEBUG_CHANNEL(xrender);
42

43
#ifdef SONAME_LIBXRENDER
44

45 46
static BOOL X11DRV_XRender_Installed = FALSE;

47
#include <X11/Xlib.h>
48
#include <X11/extensions/Xrender.h>
49

50 51 52

enum drawable_depth_type {mono_drawable, color_drawable};
static XRenderPictFormat *pict_formats[2];
53 54 55

typedef struct
{
56
    LOGFONTW lf;
57 58
    SIZE     devsize;  /* size in device coords */
    DWORD    hash;
59 60 61 62
} LFANDSIZE;

#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;
64 65 66

typedef struct
{
67 68 69 70 71 72
    GlyphSet glyphset;
    XRenderPictFormat *font_format;
    int nrealized;
    BOOL *realized;
    void **bitmaps;
    XGlyphInfo *gis;
73 74 75 76 77 78 79
} gsCacheEntryFormat;

typedef struct
{
    LFANDSIZE lfsz;
    AA_Type aa_default;
    gsCacheEntryFormat * format[AA_MAXVALUE];
80
    INT count;
81
    INT next;
82 83 84 85
} gsCacheEntry;

struct tagXRENDERINFO
{
86
    int                cache_index;
87 88 89 90 91 92 93 94 95 96 97 98 99
    Picture            pict;
};


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;

100 101 102 103
static void *xrender_handle;

#define MAKE_FUNCPTR(f) static typeof(f) * p##f;
MAKE_FUNCPTR(XRenderAddGlyphs)
104
MAKE_FUNCPTR(XRenderComposite)
105 106 107
MAKE_FUNCPTR(XRenderCompositeString8)
MAKE_FUNCPTR(XRenderCompositeString16)
MAKE_FUNCPTR(XRenderCompositeString32)
108
MAKE_FUNCPTR(XRenderCompositeText16)
109 110 111 112 113 114 115 116
MAKE_FUNCPTR(XRenderCreateGlyphSet)
MAKE_FUNCPTR(XRenderCreatePicture)
MAKE_FUNCPTR(XRenderFillRectangle)
MAKE_FUNCPTR(XRenderFindFormat)
MAKE_FUNCPTR(XRenderFindVisualFormat)
MAKE_FUNCPTR(XRenderFreeGlyphSet)
MAKE_FUNCPTR(XRenderFreePicture)
MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
117 118 119
#ifdef HAVE_XRENDERSETPICTURETRANSFORM
MAKE_FUNCPTR(XRenderSetPictureTransform)
#endif
120 121 122
MAKE_FUNCPTR(XRenderQueryExtension)
#undef MAKE_FUNCPTR

123 124 125 126 127
static CRITICAL_SECTION xrender_cs;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &xrender_cs,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
128
      0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
129 130
};
static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
131

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
#define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
          ( ( (ULONG)_x4 << 24 ) |     \
            ( (ULONG)_x3 << 16 ) |     \
            ( (ULONG)_x2 <<  8 ) |     \
              (ULONG)_x1         )

#define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')

#define GASP_GRIDFIT 0x01
#define GASP_DOGRAY  0x02

#ifdef WORDS_BIGENDIAN
#define get_be_word(x) (x)
#else
#define get_be_word(x) RtlUshortByteSwap(x)
#endif
148

149 150 151 152 153 154 155 156
/***********************************************************************
 *   X11DRV_XRender_Init
 *
 * Let's see if our XServer has the extension available
 *
 */
void X11DRV_XRender_Init(void)
{
157
    int event_base, i;
158 159
    XRenderPictFormat pf;

160 161 162 163 164
    if (client_side_with_render &&
	wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
	wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) && 
	(xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
    {
165 166 167

#define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
LOAD_FUNCPTR(XRenderAddGlyphs)
168
LOAD_FUNCPTR(XRenderComposite)
169 170 171
LOAD_FUNCPTR(XRenderCompositeString8)
LOAD_FUNCPTR(XRenderCompositeString16)
LOAD_FUNCPTR(XRenderCompositeString32)
172
LOAD_FUNCPTR(XRenderCompositeText16)
173 174 175 176 177 178 179 180 181 182
LOAD_FUNCPTR(XRenderCreateGlyphSet)
LOAD_FUNCPTR(XRenderCreatePicture)
LOAD_FUNCPTR(XRenderFillRectangle)
LOAD_FUNCPTR(XRenderFindFormat)
LOAD_FUNCPTR(XRenderFindVisualFormat)
LOAD_FUNCPTR(XRenderFreeGlyphSet)
LOAD_FUNCPTR(XRenderFreePicture)
LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
LOAD_FUNCPTR(XRenderQueryExtension)
#undef LOAD_FUNCPTR
183 184 185 186 187 188
#ifdef HAVE_XRENDERSETPICTURETRANSFORM
#define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
#undef LOAD_OPTIONAL_FUNCPTR
#endif

189

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 195
            pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
            if(!pict_formats[color_drawable])
196 197 198 199 200 201 202 203
            {
                /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
                if (visual->class == DirectColor)
                {
                    XVisualInfo info;
                    if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
                                          screen_depth, TrueColor, &info ))
                    {
204 205
                        pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
                        if (pict_formats[color_drawable]) visual = info.visual;
206 207 208
                    }
                }
            }
209
            if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
210
            {
211 212 213 214 215 216 217 218 219 220 221 222
                wine_tsx11_unlock();
                WINE_MESSAGE(
                    "Wine has detected that you probably have a buggy version\n"
                    "of libXrender.so .  Because of this client side font rendering\n"
                    "will be disabled.  Please upgrade this library.\n");
                X11DRV_XRender_Installed = FALSE;
                return;
            }
            pf.type = PictTypeDirect;
            pf.depth = 1;
            pf.direct.alpha = 0;
            pf.direct.alphaMask = 1;
223 224 225 226
            pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
                                                             PictFormatDepth | PictFormatAlpha |
                                                             PictFormatAlphaMask, &pf, 0);
            if(!pict_formats[mono_drawable]) {
227 228 229
                ERR("mono_format == NULL?\n");
                X11DRV_XRender_Installed = FALSE;
            }
230 231 232 233
            if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
                WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
                X11DRV_XRender_Installed = FALSE;
            }
234 235 236 237 238 239 240
        }
        wine_tsx11_unlock();
    }

sym_not_found:
    if(X11DRV_XRender_Installed || client_side_with_core)
    {
241 242 243 244 245 246 247 248 249 250
	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;
251
	using_client_side_fonts = 1;
252

253 254 255 256 257 258 259 260 261 262
	if(!X11DRV_XRender_Installed) {
	    TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
	    if(screen_depth <= 8 || !client_side_antialias_with_core)
	        antialias = 0;
	} else {
	    if(screen_depth <= 8 || !client_side_antialias_with_render)
	        antialias = 0;
	}
    }
    else TRACE("Using X11 core fonts\n");
263 264 265 266 267
}

static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
{
  if(p1->hash != p2->hash) return TRUE;
268
  if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
269 270 271 272
  if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
  return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
}

273
#if 0
274 275 276 277
static void walk_cache(void)
{
  int i;

278
  EnterCriticalSection(&xrender_cs);
279 280
  for(i=mru; i >= 0; i = glyphsetCache[i].next)
    TRACE("item %d\n", i);
281
  LeaveCriticalSection(&xrender_cs);
282
}
283
#endif
284

285
static int LookupEntry(LFANDSIZE *plfsz)
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
{
  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);
304
      return i;
305 306 307 308
    }
    prev_i = i;
  }
  TRACE("font not in cache\n");
309
  return -1;
310 311
}

312 313
static void FreeEntry(int entry)
{
314 315 316 317
    int i, format;
  
    for(format = 0; format < AA_MAXVALUE; format++) {
        gsCacheEntryFormat * formatEntry;
318

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
        if( !glyphsetCache[entry].format[format] )
            continue;

        formatEntry = glyphsetCache[entry].format[format];

        if(formatEntry->glyphset) {
            wine_tsx11_lock();
            pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
            wine_tsx11_unlock();
            formatEntry->glyphset = 0;
        }
        if(formatEntry->nrealized) {
            HeapFree(GetProcessHeap(), 0, formatEntry->realized);
            formatEntry->realized = NULL;
            if(formatEntry->bitmaps) {
                for(i = 0; i < formatEntry->nrealized; i++)
335
                    HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
336 337 338
                HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
                formatEntry->bitmaps = NULL;
            }
339 340
            HeapFree(GetProcessHeap(), 0, formatEntry->gis);
            formatEntry->gis = NULL;
341 342 343 344 345
            formatEntry->nrealized = 0;
        }

        HeapFree(GetProcessHeap(), 0, formatEntry);
        glyphsetCache[entry].format[format] = NULL;
346 347 348
    }
}

349
static int AllocEntry(void)
350 351 352 353 354 355 356
{
  int best = -1, prev_best = -1, i, prev_i = -1;

  if(lastfree >= 0) {
    assert(glyphsetCache[lastfree].count == -1);
    glyphsetCache[lastfree].count = 1;
    best = lastfree;
357
    lastfree = glyphsetCache[lastfree].next;
358 359 360 361 362
    assert(best != mru);
    glyphsetCache[best].next = mru;
    mru = best;

    TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
363
    return mru;
364 365 366 367 368 369 370 371 372 373 374 375
  }

  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);
376
    FreeEntry(best);
377 378 379 380 381 382 383 384
    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);
    }
385
    return mru;
386 387 388
  }

  TRACE("Growing cache\n");
389 390 391
  
  if (glyphsetCache)
    glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
392 393 394
			      glyphsetCache,
			      (glyphsetCacheSize + INIT_CACHE_SIZE)
			      * sizeof(*glyphsetCache));
395 396 397 398 399
  else
    glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
			      (glyphsetCacheSize + INIT_CACHE_SIZE)
			      * sizeof(*glyphsetCache));

400 401 402 403 404 405 406 407 408 409 410 411 412
  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);
413
  return mru;
414
}
415

416 417 418
static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
{
    DWORD size;
419
    WORD *gasp, *buffer;
420 421 422 423 424 425 426 427 428 429
    WORD num_recs;
    DWORD ppem;
    TEXTMETRICW tm;

    *flags = 0;

    size = GetFontData(physDev->hdc, MS_GASP_TAG,  0, NULL, 0);
    if(size == GDI_ERROR)
        return FALSE;

430
    gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
    GetFontData(physDev->hdc, MS_GASP_TAG,  0, gasp, size);

    GetTextMetricsW(physDev->hdc, &tm);
    ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));

    gasp++;
    num_recs = get_be_word(*gasp);
    gasp++;
    while(num_recs--)
    {
        *flags = get_be_word(*(gasp + 1));
        if(ppem <= get_be_word(*gasp))
            break;
        gasp += 2;
    }
446
    TRACE("got flags %04x for ppem %d\n", *flags, ppem);
447

448
    HeapFree(GetProcessHeap(), 0, buffer);
449 450 451 452
    return TRUE;
}

static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
453
{
454
    int ret;
455
    int format;
456
    gsCacheEntry *entry;
457
    WORD flags;
458
    static int hinter = -1;
459

460 461 462 463 464
    if((ret = LookupEntry(plfsz)) != -1) return ret;

    ret = AllocEntry();
    entry = glyphsetCache + ret;
    entry->lfsz = *plfsz;
465 466 467
    for( format = 0; format < AA_MAXVALUE; format++ ) {
        assert( !entry->format[format] );
    }
468

469
    if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
470
    {
471 472 473 474 475 476 477
        if(hinter == -1)
        {
            RASTERIZER_STATUS status;
            GetRasterizerCaps(&status, sizeof(status));
            hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
        }
        if(!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
478 479 480 481
            entry->aa_default = AA_Grey;
        else
            entry->aa_default = AA_None;
    }
482
    else
483
        entry->aa_default = AA_None;
484 485

    return ret;
486 487
}

488
static void dec_ref_cache(int index)
489
{
490 491 492 493
    assert(index >= 0);
    TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
    assert(glyphsetCache[index].count > 0);
    glyphsetCache[index].count--;
494 495 496 497 498 499 500
}

static void lfsz_calc_hash(LFANDSIZE *plfsz)
{
  DWORD hash = 0, *ptr;
  int i;

501 502
  hash ^= plfsz->devsize.cx;
  hash ^= plfsz->devsize.cy;
503 504
  for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
    hash ^= *ptr;
505
  for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
    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)
{
521 522 523 524 525 526
    int i;

    EnterCriticalSection(&xrender_cs);
    for(i = mru; i >= 0; i = glyphsetCache[i].next)
	FreeEntry(i);
    LeaveCriticalSection(&xrender_cs);
527 528 529 530 531 532
}


/***********************************************************************
 *   X11DRV_XRender_SelectFont
 */
533
BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
534 535 536 537
{
    LFANDSIZE lfsz;

    GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
538
    TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
539 540
	  lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
	  lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
541 542
    lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
    lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
543 544
    lfsz_calc_hash(&lfsz);

545 546
    EnterCriticalSection(&xrender_cs);
    if(!physDev->xrender) {
547 548
        physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
				     sizeof(*physDev->xrender));
549 550 551 552
	physDev->xrender->cache_index = -1;
    }
    else if(physDev->xrender->cache_index != -1)
        dec_ref_cache(physDev->xrender->cache_index);
553
    physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
554
    LeaveCriticalSection(&xrender_cs);
555 556 557 558 559 560
    return 0;
}

/***********************************************************************
 *   X11DRV_XRender_DeleteDC
 */
561
void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
562
{
563
    X11DRV_XRender_UpdateDrawable(physDev);
564

565 566 567 568
    EnterCriticalSection(&xrender_cs);
    if(physDev->xrender->cache_index != -1)
        dec_ref_cache(physDev->xrender->cache_index);
    LeaveCriticalSection(&xrender_cs);
569

570 571 572 573 574 575 576 577
    HeapFree(GetProcessHeap(), 0, physDev->xrender);
    physDev->xrender = NULL;
    return;
}

/***********************************************************************
 *   X11DRV_XRender_UpdateDrawable
 *
578
 * This gets called from X11DRV_SetDrawable and X11DRV_SelectBitmap.
579
 * It deletes the pict and tile when the drawable changes.
580
 */
581
void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
582
{
583 584 585 586 587
    wine_tsx11_lock();

    if(physDev->xrender->pict)
    {
        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;
591
    }
592 593
    wine_tsx11_unlock();

594 595 596
    return;
}

597 598 599 600 601
/************************************************************************
 *   UploadGlyph
 *
 * Helper to ExtTextOut.  Must be called inside xrender_cs
 */
602
static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
603
{
604
    unsigned int buflen;
605 606 607 608
    char *buf;
    Glyph gid;
    GLYPHMETRICS gm;
    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];
614

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
    switch(format) {
    case AA_Grey:
	ggo_format |= WINE_GGO_GRAY16_BITMAP;
	break;

    default:
        ERR("aa = %d - not implemented\n", format);
    case AA_None:
        ggo_format |= GGO_BITMAP;
	break;
    }

    buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
			      NULL);
    if(buflen == GDI_ERROR) {
        if(format != AA_None) {
            format = AA_None;
            entry->aa_default = AA_None;
            ggo_format &= ~WINE_GGO_GRAY16_BITMAP;
            ggo_format |= GGO_BITMAP;
            buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL,
                                      NULL);
        }
        if(buflen == GDI_ERROR) {
639
            WARN("GetGlyphOutlineW failed\n");
640 641 642 643 644
            return FALSE;
        }
        TRACE("Turning off antialiasing for this monochrome font\n");
    }

645 646 647 648 649 650 651
    /* If there is nothing for the current type, we create the entry. */
    if( !entry->format[format] ) {
        entry->format[format] = HeapAlloc(GetProcessHeap(),
                                          HEAP_ZERO_MEMORY,
                                          sizeof(gsCacheEntryFormat));
    }
    formatEntry = entry->format[format];
652

653 654 655 656 657
    if(formatEntry->nrealized <= glyph) {
        formatEntry->nrealized = (glyph / 128 + 1) * 128;

	if (formatEntry->realized)
	    formatEntry->realized = HeapReAlloc(GetProcessHeap(),
658
				      HEAP_ZERO_MEMORY,
659 660
				      formatEntry->realized,
				      formatEntry->nrealized * sizeof(BOOL));
661
	else
662
	    formatEntry->realized = HeapAlloc(GetProcessHeap(),
663
				      HEAP_ZERO_MEMORY,
664
				      formatEntry->nrealized * sizeof(BOOL));
665

666
	if(!X11DRV_XRender_Installed) {
667 668
	  if (formatEntry->bitmaps)
	    formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
669
				      HEAP_ZERO_MEMORY,
670 671
				      formatEntry->bitmaps,
				      formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
672
	  else
673
	    formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
674
				      HEAP_ZERO_MEMORY,
675
				      formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
676 677
        }
        if (formatEntry->gis)
678
	    formatEntry->gis = HeapReAlloc(GetProcessHeap(),
679
				   HEAP_ZERO_MEMORY,
680 681
				   formatEntry->gis,
				   formatEntry->nrealized * sizeof(formatEntry->gis[0]));
682
        else
683
	    formatEntry->gis = HeapAlloc(GetProcessHeap(),
684
				   HEAP_ZERO_MEMORY,
685
                                   formatEntry->nrealized * sizeof(formatEntry->gis[0]));
686 687
    }

688

689 690
    if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
        switch(format) {
691 692 693 694 695 696
	case AA_Grey:
	    pf.depth = 8;
	    pf.direct.alphaMask = 0xff;
	    break;

	default:
697
	    ERR("aa = %d - not implemented\n", format);
698 699 700 701 702 703 704 705 706 707
	case AA_None:
	    pf.depth = 1;
	    pf.direct.alphaMask = 1;
	    break;
	}

	pf.type = PictTypeDirect;
	pf.direct.alpha = 0;

	wine_tsx11_lock();
708
	formatEntry->font_format = pXRenderFindFormat(gdi_display,
709 710 711 712 713 714
						PictFormatType |
						PictFormatDepth |
						PictFormatAlpha |
						PictFormatAlphaMask,
						&pf, 0);

715
	formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
716
	wine_tsx11_unlock();
717 718
    }

719 720

    buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
721
    GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, NULL);
722
    formatEntry->realized[glyph] = TRUE;
723

724
    TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
	  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;

741
	if(format == AA_None) {
742 743
	    pitch = ((gi.width + 31) / 32) * 4;
	    for(i = 0; i < gi.height; i++) {
744
	        line = (unsigned char*) buf + i * pitch;
745 746 747 748 749 750 751 752
		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 {
753
	    static const char blks[] = " .:;!o*#";
754 755 756 757 758
	    char str[2];

	    str[1] = '\0';
	    pitch = ((gi.width + 3) / 4) * 4;
	    for(i = 0; i < gi.height; i++) {
759
	        line = (unsigned char*) buf + i * pitch;
760 761 762 763 764 765 766 767 768 769 770
		output[0] = '\0';
		for(j = 0; j < pitch; j++) {
		    str[0] = blks[line[j] >> 5];
		    strcat(output, str);
		}
		strcat(output, "\n");
		TRACE(output);
	    }
	}
    }

771

772 773
    if(formatEntry->glyphset) {
        if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
774
	    unsigned char *byte = (unsigned char*) buf, c;
775
	    int i = buflen;
776

777 778
	    while(i--) {
	        c = *byte;
779

780 781 782 783
		/* 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);
784

785 786
		*byte++ = c;
	    }
787
	}
788
	gid = glyph;
789 790 791 792 793 794 795 796 797 798 799

        /*
          XRenderCompositeText seems to ignore 0x0 glyphs when
          AA_None, which means we lose the advance width of glyphs
          like the space.  We'll pretend that such glyphs are 1x1
          bitmaps.
        */

        if(buflen == 0)
            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 804 805
	wine_tsx11_unlock();
	HeapFree(GetProcessHeap(), 0, buf);
    } else {
806
        formatEntry->bitmaps[glyph] = buf;
807
    }
808

809
    formatEntry->gis[glyph] = gi;
810

811 812 813
    return TRUE;
}

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
			    void *bitmap, XGlyphInfo *gi)
{
    unsigned char   *srcLine = bitmap, *src;
    unsigned char   bits, bitsMask;
    int             width = gi->width;
    int             stride = ((width + 31) & ~31) >> 3;
    int             height = gi->height;
    int             w;
    int             xspan, lenspan;

    TRACE("%d, %d\n", x, y);
    x -= gi->x;
    y -= gi->y;
    while (height--)
    {
        src = srcLine;
        srcLine += stride;
        w = width;
        
        bitsMask = 0x80;    /* FreeType is always MSB first */
        bits = *src++;
        
        xspan = x;
        while (w)
        {
            if (bits & bitsMask)
            {
                lenspan = 0;
                do
                {
                    lenspan++;
                    if (lenspan == w)
                        break;
                    bitsMask = bitsMask >> 1;
                    if (!bitsMask)
                    {
                        bits = *src++;
                        bitsMask = 0x80;
                    }
                } while (bits & bitsMask);
                XFillRectangle (gdi_display, physDev->drawable, 
                                physDev->gc, xspan, y, lenspan, 1);
                xspan += lenspan;
                w -= lenspan;
            }
            else
            {
                do
                {
                    w--;
                    xspan++;
                    if (!w)
                        break;
                    bitsMask = bitsMask >> 1;
                    if (!bitsMask)
                    {
                        bits = *src++;
                        bitsMask = 0x80;
                    }
                } while (!(bits & bitsMask));
            }
        }
        y++;
    }
}

static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
			    void *bitmap, XGlyphInfo *gi)
{
    unsigned char   *srcLine = bitmap, *src, bits;
    int             width = gi->width;
    int             stride = ((width + 3) & ~3);
    int             height = gi->height;
    int             w;
    int             xspan, lenspan;

    x -= gi->x;
    y -= gi->y;
    while (height--)
    {
        src = srcLine;
        srcLine += stride;
        w = width;
        
        bits = *src++;
        xspan = x;
        while (w)
        {
            if (bits >= 0x80)
            {
                lenspan = 0;
                do
                {
                    lenspan++;
                    if (lenspan == w)
                        break;
                    bits = *src++;
                } while (bits >= 0x80);
                XFillRectangle (gdi_display, physDev->drawable, 
                                physDev->gc, xspan, y, lenspan, 1);
                xspan += lenspan;
                w -= lenspan;
            }
            else
            {
                do
                {
                    w--;
                    xspan++;
                    if (!w)
                        break;
                    bits = *src++;
                } while (bits < 0x80);
            }
        }
        y++;
    }
}


static void ExamineBitfield (DWORD mask, int *shift, int *len)
{
    int s, l;

    s = 0;
    while ((mask & 1) == 0)
    {
        mask >>= 1;
        s++;
    }
    l = 0;
    while ((mask & 1) == 1)
    {
        mask >>= 1;
        l++;
    }
    *shift = s;
    *len = l;
}

static DWORD GetField (DWORD pixel, int shift, int len)
{
    pixel = pixel & (((1 << (len)) - 1) << shift);
    pixel = pixel << (32 - (shift + len)) >> 24;
    while (len < 8)
    {
        pixel |= (pixel >> len);
        len <<= 1;
    }
    return pixel;
}


static DWORD PutField (DWORD pixel, int shift, int len)
{
    shift = shift - (8 - len);
    if (len <= 8)
        pixel &= (((1 << len) - 1) << (8 - len));
    if (shift < 0)
        pixel >>= -shift;
    else
        pixel <<= shift;
    return pixel;
}

static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
981
			    int color)
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
{
    int             r_shift, r_len;
    int             g_shift, g_len;
    int             b_shift, b_len;
    BYTE            *maskLine, *mask, m;
    int             maskStride;
    DWORD           pixel;
    int             width, height;
    int             w, tx;
    BYTE            src_r, src_g, src_b;

    x -= gi->x;
    y -= gi->y;
    width = gi->width;
    height = gi->height;

    maskLine = (unsigned char *) bitmap;
    maskStride = (width + 3) & ~3;

    ExamineBitfield (image->red_mask, &r_shift, &r_len);
    ExamineBitfield (image->green_mask, &g_shift, &g_len);
    ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1004 1005 1006 1007 1008

    src_r = GetField(color, r_shift, r_len);
    src_g = GetField(color, g_shift, g_len);
    src_b = GetField(color, b_shift, b_len);
    
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    for(; height--; y++)
    {
        mask = maskLine;
        maskLine += maskStride;
        w = width;
        tx = x;

	if(y < 0) continue;
	if(y >= image->height) break;

        for(; w--; tx++)
        {
	    if(tx >= image->width) break;

            m = *mask++;
	    if(tx < 0) continue;

	    if (m == 0xff)
1027
		XPutPixel (image, tx, y, color);
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
	    else if (m)
	    {
	        BYTE r, g, b;

		pixel = XGetPixel (image, tx, y);

		r = GetField(pixel, r_shift, r_len);
		r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
		g = GetField(pixel, g_shift, g_len);
		g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
		b = GetField(pixel, b_shift, b_len);
		b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;

		pixel = (PutField (r, r_shift, r_len) |
			 PutField (g, g_shift, g_len) |
			 PutField (b, b_shift, b_len));
		XPutPixel (image, tx, y, pixel);
	    }
        }
    }
}

1050 1051 1052
/*************************************************************
 *                 get_tile_pict
 *
Austin English's avatar
Austin English committed
1053
 * Returns an appropriate Picture for tiling the text colour.
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
 * Call and use result within the xrender_cs
 */
static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
{
    static struct
    {
        Pixmap xpm;
        Picture pict;
        int current_color;
    } tiles[2], *tile;
    XRenderColor col;

    tile = &tiles[type];

    if(!tile->xpm)
    {
        XRenderPictureAttributes pa;

        wine_tsx11_lock();
        tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);

        pa.repeat = True;
        tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
        wine_tsx11_unlock();

        /* init current_color to something different from text_pixel */
        tile->current_color = ~text_pixel;

        if(type == mono_drawable)
        {
            /* for a 1bpp bitmap we always need a 1 in the tile */
            col.red = col.green = col.blue = 0;
            col.alpha = 0xffff;
            wine_tsx11_lock();
            pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
            wine_tsx11_unlock();
        }
    }

    if(text_pixel != tile->current_color && type == color_drawable)
    {
        /* Map 0 -- 0xff onto 0 -- 0xffff */
        int r_shift, r_len;
        int g_shift, g_len;
        int b_shift, b_len;

        ExamineBitfield (visual->red_mask, &r_shift, &r_len );
        ExamineBitfield (visual->green_mask, &g_shift, &g_len);
        ExamineBitfield (visual->blue_mask, &b_shift, &b_len);

        col.red = GetField(text_pixel, r_shift, r_len);
        col.red |= col.red << 8;
        col.green = GetField(text_pixel, g_shift, g_len);
        col.green |= col.green << 8;
        col.blue = GetField(text_pixel, b_shift, b_len);
        col.blue |= col.blue << 8;
1110
        col.alpha = 0xffff;
1111 1112 1113 1114 1115 1116 1117 1118 1119

        wine_tsx11_lock();
        pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
        wine_tsx11_unlock();
        tile->current_color = text_pixel;
    }
    return tile->pict;
}

1120 1121 1122 1123 1124
static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
{
    return 1;
}

1125 1126 1127
/***********************************************************************
 *   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 )
1131
{
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 1144 1145
    unsigned int idx;
    double cosEsc, sinEsc;
    LOGFONTW lf;
1146
    enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1147
    Picture tile_pict = 0;
1148 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 1152 1153 1154 1155 1156
        TRACE("bitmap is not a DIB\n");
    }
    else if (bmp.dsBmih.biBitCount <= 8) {
        TRACE("Disabling antialiasing\n");
        disable_antialias = TRUE;
    }
1157 1158 1159 1160

    xgcval.function = GXcopy;
    xgcval.background = physDev->backgroundPixel;
    xgcval.fill_style = FillSolid;
1161 1162 1163
    wine_tsx11_lock();
    XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
    wine_tsx11_unlock();
1164

1165
    X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1166

1167
    if(physDev->depth == 1) {
1168
        if((physDev->textPixel & 0xffffff) == 0) {
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
	    textPixel = 0;
	    backgroundPixel = 1;
	} else {
	    textPixel = 1;
	    backgroundPixel = 0;
	}
    } else {
        textPixel = physDev->textPixel;
	backgroundPixel = physDev->backgroundPixel;
    }

1180 1181
    if(flags & ETO_OPAQUE)
    {
1182 1183 1184
        wine_tsx11_lock();
        XSetForeground( gdi_display, physDev->gc, backgroundPixel );
        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();
1188 1189
    }

1190 1191 1192
    if(count == 0)
    {
	retv = TRUE;
1193
        goto done_unlock;
1194 1195
    }

1196 1197 1198 1199 1200
    
    GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
    if(lf.lfEscapement != 0) {
        cosEsc = cos(lf.lfEscapement * M_PI / 1800);
        sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1201
    } else {
1202 1203
        cosEsc = 1;
        sinEsc = 0;
1204 1205 1206 1207
    }

    if (flags & ETO_CLIPPED)
    {
1208 1209
        HRGN clip_region;

1210
        clip_region = CreateRectRgnIndirect( lprect );
1211 1212 1213 1214 1215
        /* make a copy of the current device region */
        saved_region = CreateRectRgn( 0, 0, 0, 0 );
        CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
        X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
        DeleteObject( clip_region );
1216 1217
    }

1218 1219 1220 1221
    if(X11DRV_XRender_Installed) {
        if(!physDev->xrender->pict) {
	    XRenderPictureAttributes pa;
	    pa.subwindow_mode = IncludeInferiors;
1222

1223 1224 1225
	    wine_tsx11_lock();
	    physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
							   physDev->drawable,
1226
                                                           pict_formats[depth_type],
1227 1228
							   CPSubwindowMode, &pa);
	    wine_tsx11_unlock();
1229

1230 1231
	    TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
                  physDev->xrender->pict, hdc, physDev->drawable);
1232
	} else {
1233 1234
	    TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
                  physDev->xrender->pict, hdc, physDev->drawable);
1235
	}
1236

1237
	if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1238 1239 1240
	{
	    wine_tsx11_lock();
	    pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1241
					      physDev->dc_rect.left, physDev->dc_rect.top,
1242 1243 1244 1245 1246
					      (XRectangle *)data->Buffer, data->rdh.nCount );
	    wine_tsx11_unlock();
	    HeapFree( GetProcessHeap(), 0, data );
	}
    }
1247

1248 1249
    EnterCriticalSection(&xrender_cs);

1250
    entry = glyphsetCache + physDev->xrender->cache_index;
1251
    if( disable_antialias == FALSE )
1252 1253
        aa_type = entry->aa_default;
    formatEntry = entry->format[aa_type];
1254

1255
    for(idx = 0; idx < count; idx++) {
1256
        if( !formatEntry ) {
1257
	    UploadGlyph(physDev, wstr[idx], aa_type);
1258 1259
            /* re-evaluate antialias since aa_default may have changed */
            if( disable_antialias == FALSE )
1260 1261
                aa_type = entry->aa_default;
            formatEntry = entry->format[aa_type];
1262
        } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1263
	    UploadGlyph(physDev, wstr[idx], aa_type);
1264 1265
	}
    }
1266 1267 1268 1269 1270 1271
    if (!formatEntry)
    {
        WARN("could not upload requested glyphs\n");
        LeaveCriticalSection(&xrender_cs);
        goto done_unlock;
    }
1272

1273
    TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1274
          physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1275

1276 1277 1278 1279 1280
    if(X11DRV_XRender_Installed)
    {
        XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
        INT offset = 0;
        POINT desired, current;
1281
        int render_op = PictOpOver;
1282

1283 1284 1285 1286 1287 1288 1289 1290
        /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
           So we pass zeros to the function and move to our starting position using the first
           element of the elts array. */

        desired.x = physDev->dc_rect.left + x;
        desired.y = physDev->dc_rect.top + y;
        current.x = current.y = 0;

1291 1292 1293 1294 1295 1296 1297
        tile_pict = get_tile_pict(depth_type, physDev->textPixel);

	/* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
	 */
	if((depth_type == mono_drawable) && (textPixel == 0))
	    render_op = PictOpOutReverse; /* This gives us 'black' text */

1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
        for(idx = 0; idx < count; idx++)
        {
            elts[idx].glyphset = formatEntry->glyphset;
            elts[idx].chars = wstr + idx;
            elts[idx].nchars = 1;
            elts[idx].xOff = desired.x - current.x;
            elts[idx].yOff = desired.y - current.y;

            current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
            current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);

            if(!lpDx)
            {
                desired.x += formatEntry->gis[wstr[idx]].xOff;
                desired.y += formatEntry->gis[wstr[idx]].yOff;
            }
            else
            {
                offset += lpDx[idx];
                desired.x = physDev->dc_rect.left + x + offset * cosEsc;
                desired.y = physDev->dc_rect.top  + y - offset * sinEsc;
            }
        }
        wine_tsx11_lock();
        pXRenderCompositeText16(gdi_display, render_op,
1323
                                tile_pict,
1324 1325 1326 1327 1328
                                physDev->xrender->pict,
                                formatEntry->font_format,
                                0, 0, 0, 0, elts, count);
        wine_tsx11_unlock();
        HeapFree(GetProcessHeap(), 0, elts);
1329
    } else {
1330
        INT offset = 0, xoff = 0, yoff = 0;
1331 1332 1333
        wine_tsx11_lock();
	XSetForeground( gdi_display, physDev->gc, textPixel );

1334 1335 1336 1337 1338 1339 1340 1341 1342
        if(aa_type == AA_None || physDev->depth == 1)
        {
            void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);

            if(aa_type == AA_None)
                sharp_glyph_fn = SharpGlyphMono;
            else
                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 1347 1348 1349
			       formatEntry->bitmaps[wstr[idx]],
			       &formatEntry->gis[wstr[idx]]);
		if(lpDx) {
		    offset += lpDx[idx];
1350 1351 1352
		    xoff = offset * cosEsc;
		    yoff = offset * -sinEsc;
		} else {
1353 1354
		    xoff += formatEntry->gis[wstr[idx]].xOff;
		    yoff += formatEntry->gis[wstr[idx]].yOff;
1355 1356 1357 1358 1359 1360 1361
		}
	    }
	} else {
	    XImage *image;
	    int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
	    RECT extents = {0, 0, 0, 0};
	    POINT cur = {0, 0};
1362 1363 1364
            int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
            int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;

1365 1366 1367
	    TRACE("drawable %dx%d\n", w, h);

	    for(idx = 0; idx < count; idx++) {
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
	        if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
		    extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
		if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
		    extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
		if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
		    extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
		if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
		    extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
		if(lpDx) {
		    offset += lpDx[idx];
1378 1379 1380
		    cur.x = offset * cosEsc;
		    cur.y = offset * -sinEsc;
		} else {
1381 1382
		    cur.x += formatEntry->gis[wstr[idx]].xOff;
		    cur.y += formatEntry->gis[wstr[idx]].yOff;
1383 1384
		}
	    }
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);
1387

1388 1389
	    if(physDev->dc_rect.left + x + extents.left >= 0) {
	        image_x = physDev->dc_rect.left + x + extents.left;
1390 1391 1392
		image_off_x = 0;
	    } else {
	        image_x = 0;
1393
		image_off_x = physDev->dc_rect.left + x + extents.left;
1394
	    }
1395 1396
	    if(physDev->dc_rect.top + y + extents.top >= 0) {
	        image_y = physDev->dc_rect.top + y + extents.top;
1397 1398 1399
		image_off_y = 0;
	    } else {
	        image_y = 0;
1400
		image_off_y = physDev->dc_rect.top + y + extents.top;
1401
	    }
1402 1403
	    if(physDev->dc_rect.left + x + extents.right < w)
	        image_w = physDev->dc_rect.left + x + extents.right - image_x;
1404 1405
	    else
	        image_w = w - image_x;
1406 1407
	    if(physDev->dc_rect.top + y + extents.bottom < h)
	        image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421
	    else
	        image_h = h - image_y;

	    if(image_w <= 0 || image_h <= 0) goto no_image;

	    X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
	    image = XGetImage(gdi_display, physDev->drawable,
			      image_x, image_y, image_w, image_h,
			      AllPlanes, ZPixmap);
	    X11DRV_check_error();

	    TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
		  gdi_display, (int)physDev->drawable, image_x, image_y,
		  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 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449
		GC gc;
		XGCValues gcv;

		gcv.graphics_exposures = False;
		gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
		XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
			  image_w, image_h, 0, 0);
		XFreeGC(gdi_display, gc);
		X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
		image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
				  ZPixmap);
		X11DRV_check_error();
		XFreePixmap(gdi_display, xpm);
	    }
	    if(!image) goto no_image;

	    image->red_mask = visual->red_mask;
	    image->green_mask = visual->green_mask;
	    image->blue_mask = visual->blue_mask;

	    offset = xoff = yoff = 0;
	    for(idx = 0; idx < count; idx++) {
	        SmoothGlyphGray(image, xoff + image_off_x - extents.left,
				yoff + image_off_y - extents.top,
1450 1451
				formatEntry->bitmaps[wstr[idx]],
				&formatEntry->gis[wstr[idx]],
1452
				physDev->textPixel);
1453 1454
		if(lpDx) {
		    offset += lpDx[idx];
1455 1456 1457
		    xoff = offset * cosEsc;
		    yoff = offset * -sinEsc;
		} else {
1458 1459
		    xoff += formatEntry->gis[wstr[idx]].xOff;
		    yoff += formatEntry->gis[wstr[idx]].yOff;
1460 1461 1462 1463 1464
		}
	    }
	    XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
		      image_x, image_y, image_w, image_h);
	    XDestroyImage(image);
1465
	}
1466
    no_image:
1467
	wine_tsx11_unlock();
1468
    }
1469 1470
    LeaveCriticalSection(&xrender_cs);

1471
    if (flags & ETO_CLIPPED)
1472 1473 1474 1475 1476
    {
        /* restore the device region */
        X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
        DeleteObject( saved_region );
    }
1477

1478 1479
    retv = TRUE;

1480
done_unlock:
1481
    X11DRV_UnlockDIBSection( physDev, TRUE );
1482
    return retv;
1483 1484
}

1485 1486 1487
/******************************************************************************
 * AlphaBlend         (x11drv.@)
 */
1488 1489
BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
                       X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1490
                       BLENDFUNCTION blendfn)
1491 1492 1493
{
    XRenderPictureAttributes pa;
    XRenderPictFormat *src_format;
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521
    XRenderPictFormat argb32_templ = {
        0,                          /* id */
        PictTypeDirect,             /* type */
        32,                         /* depth */
        {                           /* direct */
            16,                     /* direct.red */
            0xff,                   /* direct.redMask */
            8,                      /* direct.green */
            0xff,                   /* direct.greenMask */
            0,                      /* direct.blue */
            0xff,                   /* direct.blueMask */
            24,                     /* direct.alpha */
            0xff,                   /* direct.alphaMask */
        },
        0,                          /* colormap */
    };
    unsigned long argb32_templ_mask = 
        PictFormatType |
        PictFormatDepth |
        PictFormatRed |
        PictFormatRedMask |
        PictFormatGreen |
        PictFormatGreenMask |
        PictFormatBlue |
        PictFormatBlueMask |
        PictFormatAlpha |
        PictFormatAlphaMask;

1522 1523
    Picture dst_pict, src_pict;
    Pixmap xpm;
1524
    DIBSECTION dib;
1525 1526 1527
    XImage *image;
    GC gc;
    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;
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557

    if(!X11DRV_XRender_Installed) {
        FIXME("Unable to AlphaBlend without Xrender\n");
        return FALSE;
    }
    pts[0].x = xDst;
    pts[0].y = yDst;
    pts[1].x = xDst + widthDst;
    pts[1].y = yDst + heightDst;
    LPtoDP(devDst->hdc, pts, 2);
    xDst      = pts[0].x;
    yDst      = pts[0].y;
    widthDst  = pts[1].x - pts[0].x;
    heightDst = pts[1].y - pts[0].y;

    pts[0].x = xSrc;
    pts[0].y = ySrc;
    pts[1].x = xSrc + widthSrc;
    pts[1].y = ySrc + heightSrc;
    LPtoDP(devSrc->hdc, pts, 2);
    xSrc      = pts[0].x;
    ySrc      = pts[0].y;
    widthSrc  = pts[1].x - pts[0].x;
    heightSrc = pts[1].y - pts[0].y;
1558
    if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1559

1560 1561 1562 1563 1564 1565 1566
#ifndef HAVE_XRENDERSETPICTURETRANSFORM
    if(widthDst != widthSrc || heightDst != heightSrc)
#else
    if(!pXRenderSetPictureTransform)
#endif
    {
        FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1567 1568 1569
        return FALSE;
    }

1570
    if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1571
    {
1572 1573 1574 1575 1576 1577
        static BOOL out = FALSE;
        if (!out)
        {
            FIXME("not a dibsection\n");
            out = TRUE;
        }
1578 1579 1580
        return FALSE;
    }

1581 1582 1583 1584 1585 1586 1587 1588
    if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
        || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
    {
        WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

1589 1590 1591
    if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
        FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);

1592
    if(dib.dsBm.bmBitsPixel != 32) {
1593 1594 1595 1596
        FIXME("not a 32 bpp dibsection\n");
        return FALSE;
    }
    dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1597

1598
    if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1599
        top_down = TRUE;
1600
        dstbits += widthSrc * (heightSrc - 1);
1601
        y2 = ySrc;
1602
        y = y2 + heightSrc - 1;
1603
    }
1604 1605 1606
    else
    {
        y = dib.dsBmih.biHeight - ySrc - 1;
1607
        y2 = y - heightSrc + 1;
1608
    }
1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636

    if (blendfn.AlphaFormat & AC_SRC_ALPHA)
    {
        for(; y >= y2; y--)
        {
            memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
                   widthSrc * 4);
            dstbits += (top_down ? -1 : 1) * widthSrc;
        }
    }
    else
    {
        DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
        int x;

        for(; y >= y2; y--)
        {
            DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
            for (x = 0; x < widthSrc; x++)
            {
                DWORD argb = *srcbits++;
                argb = (argb & 0xffffff) | source_alpha;
                *dstbits++ = argb;
            }
            if (top_down)  /* we traversed the row forward so we should go back by two rows */
                dstbits -= 2 * widthSrc;
        }

1637 1638
    }

1639 1640
    rgndata = X11DRV_GetRegionData( devDst->region, 0 );

1641 1642
    wine_tsx11_lock();
    image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
Mike McCormack's avatar
Mike McCormack committed
1643
                         (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1644

1645 1646 1647 1648 1649
    /*
      Avoid using XRenderFindStandardFormat as older libraries don't have it
      src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
    */
    src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1650 1651 1652 1653 1654 1655 1656 1657

    TRACE("src_format %p\n", src_format);

    pa.subwindow_mode = IncludeInferiors;

    /* FIXME use devDst->xrender->pict ? */
    dst_pict = pXRenderCreatePicture(gdi_display,
                                     devDst->drawable,
1658
                                     pict_formats[dst_depth_type],
1659 1660 1661 1662
                                     CPSubwindowMode, &pa);
    TRACE("dst_pict %08lx\n", dst_pict);
    TRACE("src_drawable = %08lx\n", devSrc->drawable);
    xpm = XCreatePixmap(gdi_display,
1663
                        root_window,
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
                        widthSrc, heightSrc, 32);
    gcv.graphics_exposures = False;
    gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
    TRACE("xpm = %08lx\n", xpm);
    XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);

    src_pict = pXRenderCreatePicture(gdi_display,
                                     xpm, src_format,
                                     CPSubwindowMode, &pa);
    TRACE("src_pict %08lx\n", src_pict);

1675
    if (rgndata)
1676 1677
    {
        pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1678
                                          devDst->dc_rect.left, devDst->dc_rect.top,
1679 1680 1681 1682 1683
                                          (XRectangle *)rgndata->Buffer, 
                                          rgndata->rdh.nCount );
        HeapFree( GetProcessHeap(), 0, rgndata );
    }

1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695
#ifdef HAVE_XRENDERSETPICTURETRANSFORM
    if(widthDst != widthSrc || heightDst != heightSrc) {
        double xscale = widthSrc/(double)widthDst;
        double yscale = heightSrc/(double)heightDst;
        XTransform xform = {{
            { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
            { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
            { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
        }};
        pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
    }
#endif
1696
    pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1697
                      0, 0, 0, 0,
1698
                      xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712


    pXRenderFreePicture(gdi_display, src_pict);
    XFreePixmap(gdi_display, xpm);
    XFreeGC(gdi_display, gc);
    pXRenderFreePicture(gdi_display, dst_pict);
    image->data = NULL;
    XDestroyImage(image);

    wine_tsx11_unlock();
    HeapFree(GetProcessHeap(), 0, data);
    return TRUE;
}

1713
#else /* SONAME_LIBXRENDER */
1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724

void X11DRV_XRender_Init(void)
{
    TRACE("XRender support not compiled in.\n");
    return;
}

void X11DRV_XRender_Finalize(void)
{
}

1725
BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1726 1727 1728 1729 1730
{
  assert(0);
  return FALSE;
}

1731
void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1732 1733 1734 1735 1736
{
  assert(0);
  return;
}

1737
BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1738
				const RECT *lprect, LPCWSTR wstr, UINT count,
1739
				const INT *lpDx )
1740 1741 1742 1743 1744
{
  assert(0);
  return FALSE;
}

1745
void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1746 1747 1748 1749 1750
{
  assert(0);
  return;
}

1751 1752 1753
/******************************************************************************
 * AlphaBlend         (x11drv.@)
 */
1754 1755
BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
                       X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1756
                       BLENDFUNCTION blendfn)
1757 1758 1759 1760 1761
{
  FIXME("not supported - XRENDER headers were missing at compile time\n");
  return FALSE;
}

1762
#endif /* SONAME_LIBXRENDER */