1 /* vi:set ts=8 sts=4 sw=4 noet: */ 2 /* 3 * Author: MURAOKA Taro <[email protected]> 4 * 5 * Contributors: 6 * - Ken Takata 7 * - Yasuhiro Matsumoto 8 * 9 * Copyright (C) 2013 MURAOKA Taro <[email protected]> 10 * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. 11 */ 12 13 #define WIN32_LEAN_AND_MEAN 14 15 #ifndef DYNAMIC_DIRECTX 16 # if WINVER < 0x0600 17 # error WINVER must be 0x0600 or above to use DirectWrite(DirectX) 18 # endif 19 #endif 20 21 #include <windows.h> 22 #include <crtdbg.h> 23 #include <assert.h> 24 #include <math.h> 25 #include <d2d1.h> 26 #include <d2d1helper.h> 27 28 // Disable these macros to compile with old VC and newer SDK (V8.1 or later). 29 #if defined(_MSC_VER) && (_MSC_VER < 1700) 30 # define _COM_Outptr_ __out 31 # define _In_reads_(s) 32 # define _In_reads_opt_(s) 33 # define _Maybenull_ 34 # define _Out_writes_(s) 35 # define _Out_writes_opt_(s) 36 # define _Out_writes_to_(x, y) 37 # define _Out_writes_to_opt_(x, y) 38 # define _Outptr_ 39 #endif 40 41 #ifdef FEAT_DIRECTX_COLOR_EMOJI 42 # include <dwrite_2.h> 43 #else 44 # include <dwrite.h> 45 #endif 46 47 #include "gui_dwrite.h" 48 49 #ifdef __MINGW32__ 50 # define __maybenull SAL__maybenull 51 # define __in SAL__in 52 # define __out SAL__out 53 #endif 54 55 #if (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L) 56 # define FINAL final 57 #else 58 # define FINAL 59 #endif 60 61 #ifdef DYNAMIC_DIRECTX 62 extern "C" HINSTANCE vimLoadLib(char *name); 63 64 typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int); 65 typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE, 66 REFIID, const D2D1_FACTORY_OPTIONS *, void **); 67 typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE, 68 REFIID, IUnknown **); 69 70 static HINSTANCE hD2D1DLL = NULL; 71 static HINSTANCE hDWriteDLL = NULL; 72 73 static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL; 74 static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL; 75 static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL; 76 77 #define GetUserDefaultLocaleName (*pGetUserDefaultLocaleName) 78 #define D2D1CreateFactory (*pD2D1CreateFactory) 79 #define DWriteCreateFactory (*pDWriteCreateFactory) 80 81 static void 82 unload(HINSTANCE &hinst) 83 { 84 if (hinst != NULL) 85 { 86 FreeLibrary(hinst); 87 hinst = NULL; 88 } 89 } 90 #endif // DYNAMIC_DIRECTX 91 92 template <class T> inline void SafeRelease(T **ppT) 93 { 94 if (*ppT) 95 { 96 (*ppT)->Release(); 97 *ppT = NULL; 98 } 99 } 100 101 static DWRITE_PIXEL_GEOMETRY 102 ToPixelGeometry(int value) 103 { 104 switch (value) 105 { 106 default: 107 case 0: 108 return DWRITE_PIXEL_GEOMETRY_FLAT; 109 case 1: 110 return DWRITE_PIXEL_GEOMETRY_RGB; 111 case 2: 112 return DWRITE_PIXEL_GEOMETRY_BGR; 113 } 114 } 115 116 static int 117 ToInt(DWRITE_PIXEL_GEOMETRY value) 118 { 119 switch (value) 120 { 121 case DWRITE_PIXEL_GEOMETRY_FLAT: 122 return 0; 123 case DWRITE_PIXEL_GEOMETRY_RGB: 124 return 1; 125 case DWRITE_PIXEL_GEOMETRY_BGR: 126 return 2; 127 default: 128 return -1; 129 } 130 } 131 132 static DWRITE_RENDERING_MODE 133 ToRenderingMode(int value) 134 { 135 switch (value) 136 { 137 default: 138 case 0: 139 return DWRITE_RENDERING_MODE_DEFAULT; 140 case 1: 141 return DWRITE_RENDERING_MODE_ALIASED; 142 case 2: 143 return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; 144 case 3: 145 return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL; 146 case 4: 147 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; 148 case 5: 149 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; 150 case 6: 151 return DWRITE_RENDERING_MODE_OUTLINE; 152 } 153 } 154 155 static D2D1_TEXT_ANTIALIAS_MODE 156 ToTextAntialiasMode(int value) 157 { 158 switch (value) 159 { 160 default: 161 case 0: 162 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 163 case 1: 164 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; 165 case 2: 166 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; 167 case 3: 168 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED; 169 } 170 } 171 172 static int 173 ToInt(DWRITE_RENDERING_MODE value) 174 { 175 switch (value) 176 { 177 case DWRITE_RENDERING_MODE_DEFAULT: 178 return 0; 179 case DWRITE_RENDERING_MODE_ALIASED: 180 return 1; 181 case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC: 182 return 2; 183 case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL: 184 return 3; 185 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL: 186 return 4; 187 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC: 188 return 5; 189 case DWRITE_RENDERING_MODE_OUTLINE: 190 return 6; 191 default: 192 return -1; 193 } 194 } 195 196 class FontCache { 197 public: 198 struct Item { 199 HFONT hFont; 200 IDWriteTextFormat* pTextFormat; 201 DWRITE_FONT_WEIGHT fontWeight; 202 DWRITE_FONT_STYLE fontStyle; 203 Item() : hFont(NULL), pTextFormat(NULL) {} 204 }; 205 206 private: 207 int mSize; 208 Item *mItems; 209 210 public: 211 FontCache(int size = 2) : 212 mSize(size), 213 mItems(new Item[size]) 214 { 215 } 216 217 ~FontCache() 218 { 219 for (int i = 0; i < mSize; ++i) 220 SafeRelease(&mItems[i].pTextFormat); 221 delete[] mItems; 222 } 223 224 bool get(HFONT hFont, Item &item) 225 { 226 int n = find(hFont); 227 if (n < 0) 228 return false; 229 item = mItems[n]; 230 slide(n); 231 return true; 232 } 233 234 void put(const Item& item) 235 { 236 int n = find(item.hFont); 237 if (n < 0) 238 n = mSize - 1; 239 if (mItems[n].pTextFormat != item.pTextFormat) 240 { 241 SafeRelease(&mItems[n].pTextFormat); 242 item.pTextFormat->AddRef(); 243 } 244 mItems[n] = item; 245 slide(n); 246 } 247 248 private: 249 int find(HFONT hFont) 250 { 251 for (int i = 0; i < mSize; ++i) 252 { 253 if (mItems[i].hFont == hFont) 254 return i; 255 } 256 return -1; 257 } 258 259 void slide(int nextTop) 260 { 261 if (nextTop == 0) 262 return; 263 Item tmp = mItems[nextTop]; 264 for (int i = nextTop - 1; i >= 0; --i) 265 mItems[i + 1] = mItems[i]; 266 mItems[0] = tmp; 267 } 268 }; 269 270 enum DrawingMode { 271 DM_GDI = 0, 272 DM_DIRECTX = 1, 273 DM_INTEROP = 2, 274 }; 275 276 struct DWriteContext { 277 HDC mHDC; 278 RECT mBindRect; 279 DrawingMode mDMode; 280 HDC mInteropHDC; 281 bool mDrawing; 282 bool mFallbackDC; 283 284 ID2D1Factory *mD2D1Factory; 285 286 ID2D1DCRenderTarget *mRT; 287 ID2D1GdiInteropRenderTarget *mGDIRT; 288 ID2D1SolidColorBrush *mBrush; 289 ID2D1Bitmap *mBitmap; 290 291 IDWriteFactory *mDWriteFactory; 292 #ifdef FEAT_DIRECTX_COLOR_EMOJI 293 IDWriteFactory2 *mDWriteFactory2; 294 #endif 295 296 IDWriteGdiInterop *mGdiInterop; 297 IDWriteRenderingParams *mRenderingParams; 298 299 FontCache mFontCache; 300 IDWriteTextFormat *mTextFormat; 301 DWRITE_FONT_WEIGHT mFontWeight; 302 DWRITE_FONT_STYLE mFontStyle; 303 304 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode; 305 306 // METHODS 307 308 DWriteContext(); 309 310 virtual ~DWriteContext(); 311 312 HRESULT CreateDeviceResources(); 313 314 void DiscardDeviceResources(); 315 316 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, 317 IDWriteTextFormat **ppTextFormat); 318 319 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont); 320 321 void SetFont(HFONT hFont); 322 323 void Rebind(); 324 325 void BindDC(HDC hdc, const RECT *rect); 326 327 HRESULT SetDrawingMode(DrawingMode mode); 328 329 ID2D1Brush* SolidBrush(COLORREF color); 330 331 void DrawText(const WCHAR *text, int len, 332 int x, int y, int w, int h, int cellWidth, COLORREF color, 333 UINT fuOptions, const RECT *lprc, const INT *lpDx); 334 335 void FillRect(const RECT *rc, COLORREF color); 336 337 void DrawLine(int x1, int y1, int x2, int y2, COLORREF color); 338 339 void SetPixel(int x, int y, COLORREF color); 340 341 void Scroll(int x, int y, const RECT *rc); 342 343 void Flush(); 344 345 void SetRenderingParams( 346 const DWriteRenderingParams *params); 347 348 DWriteRenderingParams *GetRenderingParams( 349 DWriteRenderingParams *params); 350 }; 351 352 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN 353 { 354 private: 355 FLOAT &mAccum; 356 FLOAT mDelta; 357 FLOAT *mAdjustedAdvances; 358 359 public: 360 AdjustedGlyphRun( 361 const DWRITE_GLYPH_RUN *glyphRun, 362 FLOAT cellWidth, 363 FLOAT &accum) : 364 DWRITE_GLYPH_RUN(*glyphRun), 365 mAccum(accum), 366 mDelta(0.0f), 367 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]) 368 { 369 assert(cellWidth != 0.0f); 370 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i) 371 { 372 FLOAT orig = glyphRun->glyphAdvances[i]; 373 FLOAT adjusted = adjustToCell(orig, cellWidth); 374 mAdjustedAdvances[i] = adjusted; 375 mDelta += adjusted - orig; 376 } 377 glyphAdvances = mAdjustedAdvances; 378 } 379 380 ~AdjustedGlyphRun() 381 { 382 mAccum += mDelta; 383 delete[] mAdjustedAdvances; 384 } 385 386 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth) 387 { 388 int cellCount = int(floor(value / cellWidth + 0.5f)); 389 if (cellCount < 1) 390 cellCount = 1; 391 return cellCount * cellWidth; 392 } 393 }; 394 395 struct TextRendererContext { 396 // const fields. 397 COLORREF color; 398 FLOAT cellWidth; 399 400 // working fields. 401 FLOAT offsetX; 402 }; 403 404 class TextRenderer FINAL : public IDWriteTextRenderer 405 { 406 public: 407 TextRenderer( 408 DWriteContext* pDWC) : 409 cRefCount_(0), 410 pDWC_(pDWC) 411 { 412 AddRef(); 413 } 414 415 // add "virtual" to avoid a compiler warning 416 virtual ~TextRenderer() 417 { 418 } 419 420 IFACEMETHOD(IsPixelSnappingDisabled)( 421 __maybenull void* clientDrawingContext, 422 __out BOOL* isDisabled) 423 { 424 *isDisabled = FALSE; 425 return S_OK; 426 } 427 428 IFACEMETHOD(GetCurrentTransform)( 429 __maybenull void* clientDrawingContext, 430 __out DWRITE_MATRIX* transform) 431 { 432 // forward the render target's transform 433 pDWC_->mRT->GetTransform( 434 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform)); 435 return S_OK; 436 } 437 438 IFACEMETHOD(GetPixelsPerDip)( 439 __maybenull void* clientDrawingContext, 440 __out FLOAT* pixelsPerDip) 441 { 442 float dpiX, unused; 443 pDWC_->mRT->GetDpi(&dpiX, &unused); 444 *pixelsPerDip = dpiX / 96.0f; 445 return S_OK; 446 } 447 448 IFACEMETHOD(DrawUnderline)( 449 __maybenull void* clientDrawingContext, 450 FLOAT baselineOriginX, 451 FLOAT baselineOriginY, 452 __in DWRITE_UNDERLINE const* underline, 453 IUnknown* clientDrawingEffect) 454 { 455 return E_NOTIMPL; 456 } 457 458 IFACEMETHOD(DrawStrikethrough)( 459 __maybenull void* clientDrawingContext, 460 FLOAT baselineOriginX, 461 FLOAT baselineOriginY, 462 __in DWRITE_STRIKETHROUGH const* strikethrough, 463 IUnknown* clientDrawingEffect) 464 { 465 return E_NOTIMPL; 466 } 467 468 IFACEMETHOD(DrawInlineObject)( 469 __maybenull void* clientDrawingContext, 470 FLOAT originX, 471 FLOAT originY, 472 IDWriteInlineObject* inlineObject, 473 BOOL isSideways, 474 BOOL isRightToLeft, 475 IUnknown* clientDrawingEffect) 476 { 477 return E_NOTIMPL; 478 } 479 480 IFACEMETHOD(DrawGlyphRun)( 481 __maybenull void* clientDrawingContext, 482 FLOAT baselineOriginX, 483 FLOAT baselineOriginY, 484 DWRITE_MEASURING_MODE measuringMode, 485 __in DWRITE_GLYPH_RUN const* glyphRun, 486 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 487 IUnknown* clientDrawingEffect) 488 { 489 TextRendererContext *context = 490 reinterpret_cast<TextRendererContext*>(clientDrawingContext); 491 492 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth, 493 context->offsetX); 494 495 #ifdef FEAT_DIRECTX_COLOR_EMOJI 496 if (pDWC_->mDWriteFactory2 != NULL) 497 { 498 IDWriteColorGlyphRunEnumerator *enumerator = NULL; 499 HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun( 500 baselineOriginX + context->offsetX, 501 baselineOriginY, 502 &adjustedGlyphRun, 503 NULL, 504 DWRITE_MEASURING_MODE_GDI_NATURAL, 505 NULL, 506 0, 507 &enumerator); 508 if (SUCCEEDED(hr)) 509 { 510 // Draw by IDWriteFactory2 for color emoji 511 BOOL hasRun = TRUE; 512 enumerator->MoveNext(&hasRun); 513 while (hasRun) 514 { 515 const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun; 516 enumerator->GetCurrentRun(&colorGlyphRun); 517 518 pDWC_->mBrush->SetColor(colorGlyphRun->runColor); 519 pDWC_->mRT->DrawGlyphRun( 520 D2D1::Point2F( 521 colorGlyphRun->baselineOriginX, 522 colorGlyphRun->baselineOriginY), 523 &colorGlyphRun->glyphRun, 524 pDWC_->mBrush, 525 DWRITE_MEASURING_MODE_NATURAL); 526 enumerator->MoveNext(&hasRun); 527 } 528 SafeRelease(&enumerator); 529 return S_OK; 530 } 531 } 532 #endif 533 534 // Draw by IDWriteFactory (without color emoji) 535 pDWC_->mRT->DrawGlyphRun( 536 D2D1::Point2F( 537 baselineOriginX + context->offsetX, 538 baselineOriginY), 539 &adjustedGlyphRun, 540 pDWC_->SolidBrush(context->color), 541 DWRITE_MEASURING_MODE_NATURAL); 542 return S_OK; 543 } 544 545 public: 546 IFACEMETHOD_(unsigned long, AddRef) () 547 { 548 return InterlockedIncrement(&cRefCount_); 549 } 550 551 IFACEMETHOD_(unsigned long, Release) () 552 { 553 long newCount = InterlockedDecrement(&cRefCount_); 554 555 if (newCount == 0) 556 { 557 delete this; 558 return 0; 559 } 560 return newCount; 561 } 562 563 IFACEMETHOD(QueryInterface)( 564 IID const& riid, 565 void** ppvObject) 566 { 567 if (__uuidof(IDWriteTextRenderer) == riid) 568 { 569 *ppvObject = this; 570 } 571 else if (__uuidof(IDWritePixelSnapping) == riid) 572 { 573 *ppvObject = this; 574 } 575 else if (__uuidof(IUnknown) == riid) 576 { 577 *ppvObject = this; 578 } 579 else 580 { 581 *ppvObject = NULL; 582 return E_FAIL; 583 } 584 585 return S_OK; 586 } 587 588 private: 589 long cRefCount_; 590 DWriteContext* pDWC_; 591 }; 592 593 DWriteContext::DWriteContext() : 594 mHDC(NULL), 595 mBindRect(), 596 mDMode(DM_GDI), 597 mInteropHDC(NULL), 598 mDrawing(false), 599 mFallbackDC(false), 600 mD2D1Factory(NULL), 601 mRT(NULL), 602 mGDIRT(NULL), 603 mBrush(NULL), 604 mBitmap(NULL), 605 mDWriteFactory(NULL), 606 #ifdef FEAT_DIRECTX_COLOR_EMOJI 607 mDWriteFactory2(NULL), 608 #endif 609 mGdiInterop(NULL), 610 mRenderingParams(NULL), 611 mFontCache(8), 612 mTextFormat(NULL), 613 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL), 614 mFontStyle(DWRITE_FONT_STYLE_NORMAL), 615 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT) 616 { 617 HRESULT hr; 618 619 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 620 __uuidof(ID2D1Factory), NULL, 621 reinterpret_cast<void**>(&mD2D1Factory)); 622 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory); 623 624 if (SUCCEEDED(hr)) 625 { 626 hr = DWriteCreateFactory( 627 DWRITE_FACTORY_TYPE_SHARED, 628 __uuidof(IDWriteFactory), 629 reinterpret_cast<IUnknown**>(&mDWriteFactory)); 630 _RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr, 631 mDWriteFactory); 632 } 633 634 #ifdef FEAT_DIRECTX_COLOR_EMOJI 635 if (SUCCEEDED(hr)) 636 { 637 DWriteCreateFactory( 638 DWRITE_FACTORY_TYPE_SHARED, 639 __uuidof(IDWriteFactory2), 640 reinterpret_cast<IUnknown**>(&mDWriteFactory2)); 641 _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available"); 642 } 643 #endif 644 645 if (SUCCEEDED(hr)) 646 { 647 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop); 648 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop); 649 } 650 651 if (SUCCEEDED(hr)) 652 { 653 hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams); 654 _RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr, 655 mRenderingParams); 656 } 657 } 658 659 DWriteContext::~DWriteContext() 660 { 661 SafeRelease(&mTextFormat); 662 SafeRelease(&mRenderingParams); 663 SafeRelease(&mGdiInterop); 664 SafeRelease(&mDWriteFactory); 665 #ifdef FEAT_DIRECTX_COLOR_EMOJI 666 SafeRelease(&mDWriteFactory2); 667 #endif 668 SafeRelease(&mBitmap); 669 SafeRelease(&mBrush); 670 SafeRelease(&mGDIRT); 671 SafeRelease(&mRT); 672 SafeRelease(&mD2D1Factory); 673 } 674 675 HRESULT 676 DWriteContext::CreateDeviceResources() 677 { 678 HRESULT hr; 679 680 if (mRT != NULL) 681 return S_OK; 682 683 D2D1_RENDER_TARGET_PROPERTIES props = { 684 D2D1_RENDER_TARGET_TYPE_DEFAULT, 685 { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE }, 686 0, 0, 687 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, 688 D2D1_FEATURE_LEVEL_DEFAULT 689 }; 690 hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT); 691 _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT); 692 693 if (SUCCEEDED(hr)) 694 { 695 // This always succeeds. 696 mRT->QueryInterface( 697 __uuidof(ID2D1GdiInteropRenderTarget), 698 reinterpret_cast<void**>(&mGDIRT)); 699 _RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT); 700 } 701 702 if (SUCCEEDED(hr)) 703 { 704 hr = mRT->CreateSolidColorBrush( 705 D2D1::ColorF(D2D1::ColorF::Black), 706 &mBrush); 707 _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush); 708 } 709 710 if (SUCCEEDED(hr)) 711 Rebind(); 712 713 return hr; 714 } 715 716 void 717 DWriteContext::DiscardDeviceResources() 718 { 719 SafeRelease(&mBitmap); 720 SafeRelease(&mBrush); 721 SafeRelease(&mGDIRT); 722 SafeRelease(&mRT); 723 } 724 725 HRESULT 726 DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, 727 IDWriteTextFormat **ppTextFormat) 728 { 729 // Most of this function is copied from: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/RenderTest/TextHelpers.cpp 730 HRESULT hr = S_OK; 731 IDWriteTextFormat *pTextFormat = NULL; 732 733 IDWriteFont *font = NULL; 734 IDWriteFontFamily *fontFamily = NULL; 735 IDWriteLocalizedStrings *localizedFamilyNames = NULL; 736 float fontSize = 0; 737 738 if (SUCCEEDED(hr)) 739 { 740 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font); 741 } 742 743 // Get the font family to which this font belongs. 744 if (SUCCEEDED(hr)) 745 { 746 hr = font->GetFontFamily(&fontFamily); 747 } 748 749 // Get the family names. This returns an object that encapsulates one or 750 // more names with the same meaning but in different languages. 751 if (SUCCEEDED(hr)) 752 { 753 hr = fontFamily->GetFamilyNames(&localizedFamilyNames); 754 } 755 756 // Get the family name at index zero. If we were going to display the name 757 // we'd want to try to find one that matched the use locale, but for 758 // purposes of creating a text format object any language will do. 759 760 wchar_t familyName[100]; 761 if (SUCCEEDED(hr)) 762 { 763 hr = localizedFamilyNames->GetString(0, familyName, 764 ARRAYSIZE(familyName)); 765 } 766 767 if (SUCCEEDED(hr)) 768 { 769 // Use lfHeight of the LOGFONT as font size. 770 fontSize = float(logFont.lfHeight); 771 772 if (fontSize < 0) 773 { 774 // Negative lfHeight represents the size of the em unit. 775 fontSize = -fontSize; 776 } 777 else 778 { 779 // Positive lfHeight represents the cell height (ascent + 780 // descent). 781 DWRITE_FONT_METRICS fontMetrics; 782 font->GetMetrics(&fontMetrics); 783 784 // Convert the cell height (ascent + descent) from design units 785 // to ems. 786 float cellHeight = static_cast<float>( 787 fontMetrics.ascent + fontMetrics.descent) 788 / fontMetrics.designUnitsPerEm; 789 790 // Divide the font size by the cell height to get the font em 791 // size. 792 fontSize /= cellHeight; 793 } 794 } 795 796 // The text format includes a locale name. Ideally, this would be the 797 // language of the text, which may or may not be the same as the primary 798 // language of the user. However, for our purposes the user locale will do. 799 wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; 800 if (SUCCEEDED(hr)) 801 { 802 if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0) 803 hr = HRESULT_FROM_WIN32(GetLastError()); 804 } 805 806 if (SUCCEEDED(hr)) 807 { 808 // Create the text format object. 809 hr = mDWriteFactory->CreateTextFormat( 810 familyName, 811 NULL, // no custom font collection 812 font->GetWeight(), 813 font->GetStyle(), 814 font->GetStretch(), 815 fontSize, 816 localeName, 817 &pTextFormat); 818 } 819 820 if (SUCCEEDED(hr)) 821 hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); 822 823 if (SUCCEEDED(hr)) 824 hr = pTextFormat->SetParagraphAlignment( 825 DWRITE_PARAGRAPH_ALIGNMENT_FAR); 826 827 if (SUCCEEDED(hr)) 828 hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); 829 830 SafeRelease(&localizedFamilyNames); 831 SafeRelease(&fontFamily); 832 SafeRelease(&font); 833 834 if (SUCCEEDED(hr)) 835 *ppTextFormat = pTextFormat; 836 else 837 SafeRelease(&pTextFormat); 838 839 return hr; 840 } 841 842 HRESULT 843 DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont) 844 { 845 HRESULT hr = S_OK; 846 IDWriteTextFormat *pTextFormat = NULL; 847 848 hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat); 849 850 if (SUCCEEDED(hr)) 851 { 852 SafeRelease(&mTextFormat); 853 mTextFormat = pTextFormat; 854 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight); 855 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC 856 : DWRITE_FONT_STYLE_NORMAL; 857 } 858 859 return hr; 860 } 861 862 void 863 DWriteContext::SetFont(HFONT hFont) 864 { 865 FontCache::Item item; 866 if (mFontCache.get(hFont, item)) 867 { 868 if (item.pTextFormat != NULL) 869 { 870 item.pTextFormat->AddRef(); 871 SafeRelease(&mTextFormat); 872 mTextFormat = item.pTextFormat; 873 mFontWeight = item.fontWeight; 874 mFontStyle = item.fontStyle; 875 mFallbackDC = false; 876 } 877 else 878 mFallbackDC = true; 879 return; 880 } 881 882 HRESULT hr = E_FAIL; 883 LOGFONTW lf; 884 if (GetObjectW(hFont, sizeof(lf), &lf)) 885 hr = SetFontByLOGFONT(lf); 886 887 item.hFont = hFont; 888 if (SUCCEEDED(hr)) 889 { 890 item.pTextFormat = mTextFormat; 891 item.fontWeight = mFontWeight; 892 item.fontStyle = mFontStyle; 893 mFallbackDC = false; 894 } 895 else 896 mFallbackDC = true; 897 mFontCache.put(item); 898 } 899 900 void 901 DWriteContext::Rebind() 902 { 903 SafeRelease(&mBitmap); 904 905 mRT->BindDC(mHDC, &mBindRect); 906 mRT->SetTransform(D2D1::IdentityMatrix()); 907 908 D2D1_BITMAP_PROPERTIES props = { 909 {DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE}, 910 96.0f, 96.0f 911 }; 912 mRT->CreateBitmap( 913 D2D1::SizeU(mBindRect.right - mBindRect.left, 914 mBindRect.bottom - mBindRect.top), 915 props, &mBitmap); 916 } 917 918 void 919 DWriteContext::BindDC(HDC hdc, const RECT *rect) 920 { 921 mHDC = hdc; 922 mBindRect = *rect; 923 924 if (mRT == NULL) 925 CreateDeviceResources(); 926 else 927 { 928 Flush(); 929 Rebind(); 930 } 931 } 932 933 extern "C" void redraw_later_clear(void); 934 935 HRESULT 936 DWriteContext::SetDrawingMode(DrawingMode mode) 937 { 938 HRESULT hr = S_OK; 939 940 switch (mode) 941 { 942 default: 943 case DM_GDI: 944 if (mInteropHDC != NULL) 945 { 946 mGDIRT->ReleaseDC(NULL); 947 mInteropHDC = NULL; 948 } 949 if (mDrawing) 950 { 951 hr = mRT->EndDraw(); 952 if (hr == D2DERR_RECREATE_TARGET) 953 { 954 hr = S_OK; 955 DiscardDeviceResources(); 956 CreateDeviceResources(); 957 redraw_later_clear(); 958 } 959 mDrawing = false; 960 } 961 break; 962 963 case DM_DIRECTX: 964 if (mInteropHDC != NULL) 965 { 966 mGDIRT->ReleaseDC(NULL); 967 mInteropHDC = NULL; 968 } 969 else if (mDrawing == false) 970 { 971 CreateDeviceResources(); 972 mRT->BeginDraw(); 973 mDrawing = true; 974 } 975 break; 976 977 case DM_INTEROP: 978 if (mDrawing == false) 979 { 980 CreateDeviceResources(); 981 mRT->BeginDraw(); 982 mDrawing = true; 983 } 984 if (mInteropHDC == NULL) 985 hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC); 986 break; 987 } 988 mDMode = mode; 989 return hr; 990 } 991 992 ID2D1Brush* 993 DWriteContext::SolidBrush(COLORREF color) 994 { 995 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 | 996 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color)))); 997 return mBrush; 998 } 999 1000 void 1001 DWriteContext::DrawText(const WCHAR *text, int len, 1002 int x, int y, int w, int h, int cellWidth, COLORREF color, 1003 UINT fuOptions, const RECT *lprc, const INT *lpDx) 1004 { 1005 if (mFallbackDC) 1006 { 1007 // Fall back to GDI rendering. 1008 HRESULT hr = SetDrawingMode(DM_INTEROP); 1009 if (SUCCEEDED(hr)) 1010 { 1011 HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT); 1012 HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont); 1013 ::SetTextColor(mInteropHDC, color); 1014 ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC)); 1015 ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx); 1016 ::SelectObject(mInteropHDC, hOldFont); 1017 } 1018 return; 1019 } 1020 1021 HRESULT hr; 1022 IDWriteTextLayout *textLayout = NULL; 1023 1024 SetDrawingMode(DM_DIRECTX); 1025 1026 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat, 1027 FLOAT(w), FLOAT(h), &textLayout); 1028 1029 if (SUCCEEDED(hr)) 1030 { 1031 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) }; 1032 textLayout->SetFontWeight(mFontWeight, textRange); 1033 textLayout->SetFontStyle(mFontStyle, textRange); 1034 1035 TextRenderer renderer(this); 1036 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f }; 1037 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y)); 1038 } 1039 1040 SafeRelease(&textLayout); 1041 } 1042 1043 void 1044 DWriteContext::FillRect(const RECT *rc, COLORREF color) 1045 { 1046 if (mDMode == DM_INTEROP) 1047 { 1048 // GDI functions are used before this call. Keep using GDI. 1049 // (Switching to Direct2D causes terrible slowdown.) 1050 HBRUSH hbr = ::CreateSolidBrush(color); 1051 ::FillRect(mInteropHDC, rc, hbr); 1052 ::DeleteObject(HGDIOBJ(hbr)); 1053 } 1054 else 1055 { 1056 SetDrawingMode(DM_DIRECTX); 1057 mRT->FillRectangle( 1058 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), 1059 FLOAT(rc->right), FLOAT(rc->bottom)), 1060 SolidBrush(color)); 1061 } 1062 } 1063 1064 void 1065 DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color) 1066 { 1067 if (mDMode == DM_INTEROP) 1068 { 1069 // GDI functions are used before this call. Keep using GDI. 1070 // (Switching to Direct2D causes terrible slowdown.) 1071 HPEN hpen = ::CreatePen(PS_SOLID, 1, color); 1072 HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen)); 1073 ::MoveToEx(mInteropHDC, x1, y1, NULL); 1074 ::LineTo(mInteropHDC, x2, y2); 1075 ::SelectObject(mInteropHDC, old_pen); 1076 ::DeleteObject(HGDIOBJ(hpen)); 1077 } 1078 else 1079 { 1080 SetDrawingMode(DM_DIRECTX); 1081 mRT->DrawLine( 1082 D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f), 1083 D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f), 1084 SolidBrush(color)); 1085 } 1086 } 1087 1088 void 1089 DWriteContext::SetPixel(int x, int y, COLORREF color) 1090 { 1091 if (mDMode == DM_INTEROP) 1092 { 1093 // GDI functions are used before this call. Keep using GDI. 1094 // (Switching to Direct2D causes terrible slowdown.) 1095 ::SetPixel(mInteropHDC, x, y, color); 1096 } 1097 else 1098 { 1099 SetDrawingMode(DM_DIRECTX); 1100 // Direct2D doesn't have SetPixel API. Use DrawLine instead. 1101 mRT->DrawLine( 1102 D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f), 1103 D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f), 1104 SolidBrush(color)); 1105 } 1106 } 1107 1108 void 1109 DWriteContext::Scroll(int x, int y, const RECT *rc) 1110 { 1111 SetDrawingMode(DM_DIRECTX); 1112 mRT->Flush(); 1113 1114 D2D1_RECT_U srcRect; 1115 D2D1_POINT_2U destPoint; 1116 if (x >= 0) 1117 { 1118 srcRect.left = rc->left; 1119 srcRect.right = rc->right - x; 1120 destPoint.x = rc->left + x; 1121 } 1122 else 1123 { 1124 srcRect.left = rc->left - x; 1125 srcRect.right = rc->right; 1126 destPoint.x = rc->left; 1127 } 1128 if (y >= 0) 1129 { 1130 srcRect.top = rc->top; 1131 srcRect.bottom = rc->bottom - y; 1132 destPoint.y = rc->top + y; 1133 } 1134 else 1135 { 1136 srcRect.top = rc->top - y; 1137 srcRect.bottom = rc->bottom; 1138 destPoint.y = rc->top; 1139 } 1140 mBitmap->CopyFromRenderTarget(&destPoint, mRT, &srcRect); 1141 1142 D2D1_RECT_F destRect = { 1143 FLOAT(destPoint.x), FLOAT(destPoint.y), 1144 FLOAT(destPoint.x + srcRect.right - srcRect.left), 1145 FLOAT(destPoint.y + srcRect.bottom - srcRect.top) 1146 }; 1147 mRT->DrawBitmap(mBitmap, destRect, 1.0F, 1148 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, destRect); 1149 } 1150 1151 void 1152 DWriteContext::Flush() 1153 { 1154 SetDrawingMode(DM_GDI); 1155 } 1156 1157 void 1158 DWriteContext::SetRenderingParams( 1159 const DWriteRenderingParams *params) 1160 { 1161 if (mDWriteFactory == NULL) 1162 return; 1163 1164 IDWriteRenderingParams *renderingParams = NULL; 1165 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode = 1166 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 1167 HRESULT hr; 1168 if (params != NULL) 1169 { 1170 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma, 1171 params->enhancedContrast, params->clearTypeLevel, 1172 ToPixelGeometry(params->pixelGeometry), 1173 ToRenderingMode(params->renderingMode), &renderingParams); 1174 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode); 1175 } 1176 else 1177 hr = mDWriteFactory->CreateRenderingParams(&renderingParams); 1178 if (SUCCEEDED(hr) && renderingParams != NULL) 1179 { 1180 SafeRelease(&mRenderingParams); 1181 mRenderingParams = renderingParams; 1182 mTextAntialiasMode = textAntialiasMode; 1183 1184 Flush(); 1185 mRT->SetTextRenderingParams(mRenderingParams); 1186 mRT->SetTextAntialiasMode(mTextAntialiasMode); 1187 } 1188 } 1189 1190 DWriteRenderingParams * 1191 DWriteContext::GetRenderingParams( 1192 DWriteRenderingParams *params) 1193 { 1194 if (params != NULL && mRenderingParams != NULL) 1195 { 1196 params->gamma = mRenderingParams->GetGamma(); 1197 params->enhancedContrast = mRenderingParams->GetEnhancedContrast(); 1198 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel(); 1199 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry()); 1200 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode()); 1201 params->textAntialiasMode = mTextAntialiasMode; 1202 } 1203 return params; 1204 } 1205 1206 //////////////////////////////////////////////////////////////////////////// 1207 // PUBLIC C INTERFACES 1208 1209 void 1210 DWrite_Init(void) 1211 { 1212 #ifdef DYNAMIC_DIRECTX 1213 // Load libraries. 1214 hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll")); 1215 hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll")); 1216 if (hD2D1DLL == NULL || hDWriteDLL == NULL) 1217 { 1218 DWrite_Final(); 1219 return; 1220 } 1221 // Get address of procedures. 1222 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress( 1223 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName"); 1224 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL, 1225 "D2D1CreateFactory"); 1226 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL, 1227 "DWriteCreateFactory"); 1228 #endif 1229 } 1230 1231 void 1232 DWrite_Final(void) 1233 { 1234 #ifdef DYNAMIC_DIRECTX 1235 pGetUserDefaultLocaleName = NULL; 1236 pD2D1CreateFactory = NULL; 1237 pDWriteCreateFactory = NULL; 1238 unload(hDWriteDLL); 1239 unload(hD2D1DLL); 1240 #endif 1241 } 1242 1243 DWriteContext * 1244 DWriteContext_Open(void) 1245 { 1246 #ifdef DYNAMIC_DIRECTX 1247 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL 1248 || pDWriteCreateFactory == NULL) 1249 return NULL; 1250 #endif 1251 return new DWriteContext(); 1252 } 1253 1254 void 1255 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect) 1256 { 1257 if (ctx != NULL) 1258 ctx->BindDC(hdc, rect); 1259 } 1260 1261 void 1262 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) 1263 { 1264 if (ctx != NULL) 1265 ctx->SetFont(hFont); 1266 } 1267 1268 void 1269 DWriteContext_DrawText( 1270 DWriteContext *ctx, 1271 const WCHAR *text, 1272 int len, 1273 int x, 1274 int y, 1275 int w, 1276 int h, 1277 int cellWidth, 1278 COLORREF color, 1279 UINT fuOptions, 1280 const RECT *lprc, 1281 const INT *lpDx) 1282 { 1283 if (ctx != NULL) 1284 ctx->DrawText(text, len, x, y, w, h, cellWidth, color, 1285 fuOptions, lprc, lpDx); 1286 } 1287 1288 void 1289 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color) 1290 { 1291 if (ctx != NULL) 1292 ctx->FillRect(rc, color); 1293 } 1294 1295 void 1296 DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2, 1297 COLORREF color) 1298 { 1299 if (ctx != NULL) 1300 ctx->DrawLine(x1, y1, x2, y2, color); 1301 } 1302 1303 void 1304 DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color) 1305 { 1306 if (ctx != NULL) 1307 ctx->SetPixel(x, y, color); 1308 } 1309 1310 void 1311 DWriteContext_Scroll(DWriteContext *ctx, int x, int y, const RECT *rc) 1312 { 1313 if (ctx != NULL) 1314 ctx->Scroll(x, y, rc); 1315 } 1316 1317 void 1318 DWriteContext_Flush(DWriteContext *ctx) 1319 { 1320 if (ctx != NULL) 1321 ctx->Flush(); 1322 } 1323 1324 void 1325 DWriteContext_Close(DWriteContext *ctx) 1326 { 1327 delete ctx; 1328 } 1329 1330 void 1331 DWriteContext_SetRenderingParams( 1332 DWriteContext *ctx, 1333 const DWriteRenderingParams *params) 1334 { 1335 if (ctx != NULL) 1336 ctx->SetRenderingParams(params); 1337 } 1338 1339 DWriteRenderingParams * 1340 DWriteContext_GetRenderingParams( 1341 DWriteContext *ctx, 1342 DWriteRenderingParams *params) 1343 { 1344 if (ctx != NULL) 1345 return ctx->GetRenderingParams(params); 1346 else 1347 return NULL; 1348 } 1349