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 290 IDWriteFactory *mDWriteFactory; 291 #ifdef FEAT_DIRECTX_COLOR_EMOJI 292 IDWriteFactory2 *mDWriteFactory2; 293 #endif 294 295 IDWriteGdiInterop *mGdiInterop; 296 IDWriteRenderingParams *mRenderingParams; 297 298 FontCache mFontCache; 299 IDWriteTextFormat *mTextFormat; 300 DWRITE_FONT_WEIGHT mFontWeight; 301 DWRITE_FONT_STYLE mFontStyle; 302 303 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode; 304 305 // METHODS 306 307 DWriteContext(); 308 309 virtual ~DWriteContext(); 310 311 HRESULT CreateDeviceResources(); 312 313 void DiscardDeviceResources(); 314 315 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, 316 IDWriteTextFormat **ppTextFormat); 317 318 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont); 319 320 void SetFont(HFONT hFont); 321 322 void BindDC(HDC hdc, const RECT *rect); 323 324 HRESULT SetDrawingMode(DrawingMode mode); 325 326 ID2D1Brush* SolidBrush(COLORREF color); 327 328 void DrawText(const WCHAR *text, int len, 329 int x, int y, int w, int h, int cellWidth, COLORREF color, 330 UINT fuOptions, const RECT *lprc, const INT *lpDx); 331 332 void FillRect(const RECT *rc, COLORREF color); 333 334 void DrawLine(int x1, int y1, int x2, int y2, COLORREF color); 335 336 void SetPixel(int x, int y, COLORREF color); 337 338 void Flush(); 339 340 void SetRenderingParams( 341 const DWriteRenderingParams *params); 342 343 DWriteRenderingParams *GetRenderingParams( 344 DWriteRenderingParams *params); 345 }; 346 347 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN 348 { 349 private: 350 FLOAT &mAccum; 351 FLOAT mDelta; 352 FLOAT *mAdjustedAdvances; 353 354 public: 355 AdjustedGlyphRun( 356 const DWRITE_GLYPH_RUN *glyphRun, 357 FLOAT cellWidth, 358 FLOAT &accum) : 359 DWRITE_GLYPH_RUN(*glyphRun), 360 mAccum(accum), 361 mDelta(0.0f), 362 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]) 363 { 364 assert(cellWidth != 0.0f); 365 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i) 366 { 367 FLOAT orig = glyphRun->glyphAdvances[i]; 368 FLOAT adjusted = adjustToCell(orig, cellWidth); 369 mAdjustedAdvances[i] = adjusted; 370 mDelta += adjusted - orig; 371 } 372 glyphAdvances = mAdjustedAdvances; 373 } 374 375 ~AdjustedGlyphRun() 376 { 377 mAccum += mDelta; 378 delete[] mAdjustedAdvances; 379 } 380 381 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth) 382 { 383 int cellCount = int(floor(value / cellWidth + 0.5f)); 384 if (cellCount < 1) 385 cellCount = 1; 386 return cellCount * cellWidth; 387 } 388 }; 389 390 struct TextRendererContext { 391 // const fields. 392 COLORREF color; 393 FLOAT cellWidth; 394 395 // working fields. 396 FLOAT offsetX; 397 }; 398 399 class TextRenderer FINAL : public IDWriteTextRenderer 400 { 401 public: 402 TextRenderer( 403 DWriteContext* pDWC) : 404 cRefCount_(0), 405 pDWC_(pDWC) 406 { 407 AddRef(); 408 } 409 410 // add "virtual" to avoid a compiler warning 411 virtual ~TextRenderer() 412 { 413 } 414 415 IFACEMETHOD(IsPixelSnappingDisabled)( 416 __maybenull void* clientDrawingContext, 417 __out BOOL* isDisabled) 418 { 419 *isDisabled = FALSE; 420 return S_OK; 421 } 422 423 IFACEMETHOD(GetCurrentTransform)( 424 __maybenull void* clientDrawingContext, 425 __out DWRITE_MATRIX* transform) 426 { 427 // forward the render target's transform 428 pDWC_->mRT->GetTransform( 429 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform)); 430 return S_OK; 431 } 432 433 IFACEMETHOD(GetPixelsPerDip)( 434 __maybenull void* clientDrawingContext, 435 __out FLOAT* pixelsPerDip) 436 { 437 float dpiX, unused; 438 pDWC_->mRT->GetDpi(&dpiX, &unused); 439 *pixelsPerDip = dpiX / 96.0f; 440 return S_OK; 441 } 442 443 IFACEMETHOD(DrawUnderline)( 444 __maybenull void* clientDrawingContext, 445 FLOAT baselineOriginX, 446 FLOAT baselineOriginY, 447 __in DWRITE_UNDERLINE const* underline, 448 IUnknown* clientDrawingEffect) 449 { 450 return E_NOTIMPL; 451 } 452 453 IFACEMETHOD(DrawStrikethrough)( 454 __maybenull void* clientDrawingContext, 455 FLOAT baselineOriginX, 456 FLOAT baselineOriginY, 457 __in DWRITE_STRIKETHROUGH const* strikethrough, 458 IUnknown* clientDrawingEffect) 459 { 460 return E_NOTIMPL; 461 } 462 463 IFACEMETHOD(DrawInlineObject)( 464 __maybenull void* clientDrawingContext, 465 FLOAT originX, 466 FLOAT originY, 467 IDWriteInlineObject* inlineObject, 468 BOOL isSideways, 469 BOOL isRightToLeft, 470 IUnknown* clientDrawingEffect) 471 { 472 return E_NOTIMPL; 473 } 474 475 IFACEMETHOD(DrawGlyphRun)( 476 __maybenull void* clientDrawingContext, 477 FLOAT baselineOriginX, 478 FLOAT baselineOriginY, 479 DWRITE_MEASURING_MODE measuringMode, 480 __in DWRITE_GLYPH_RUN const* glyphRun, 481 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 482 IUnknown* clientDrawingEffect) 483 { 484 TextRendererContext *context = 485 reinterpret_cast<TextRendererContext*>(clientDrawingContext); 486 487 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth, 488 context->offsetX); 489 490 #ifdef FEAT_DIRECTX_COLOR_EMOJI 491 if (pDWC_->mDWriteFactory2 != NULL) 492 { 493 IDWriteColorGlyphRunEnumerator *enumerator = NULL; 494 HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun( 495 baselineOriginX + context->offsetX, 496 baselineOriginY, 497 &adjustedGlyphRun, 498 NULL, 499 DWRITE_MEASURING_MODE_GDI_NATURAL, 500 NULL, 501 0, 502 &enumerator); 503 if (SUCCEEDED(hr)) 504 { 505 // Draw by IDWriteFactory2 for color emoji 506 BOOL hasRun = TRUE; 507 enumerator->MoveNext(&hasRun); 508 while (hasRun) 509 { 510 const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun; 511 enumerator->GetCurrentRun(&colorGlyphRun); 512 513 pDWC_->mBrush->SetColor(colorGlyphRun->runColor); 514 pDWC_->mRT->DrawGlyphRun( 515 D2D1::Point2F( 516 colorGlyphRun->baselineOriginX, 517 colorGlyphRun->baselineOriginY), 518 &colorGlyphRun->glyphRun, 519 pDWC_->mBrush, 520 DWRITE_MEASURING_MODE_NATURAL); 521 enumerator->MoveNext(&hasRun); 522 } 523 SafeRelease(&enumerator); 524 return S_OK; 525 } 526 } 527 #endif 528 529 // Draw by IDWriteFactory (without color emoji) 530 pDWC_->mRT->DrawGlyphRun( 531 D2D1::Point2F( 532 baselineOriginX + context->offsetX, 533 baselineOriginY), 534 &adjustedGlyphRun, 535 pDWC_->SolidBrush(context->color), 536 DWRITE_MEASURING_MODE_NATURAL); 537 return S_OK; 538 } 539 540 public: 541 IFACEMETHOD_(unsigned long, AddRef) () 542 { 543 return InterlockedIncrement(&cRefCount_); 544 } 545 546 IFACEMETHOD_(unsigned long, Release) () 547 { 548 long newCount = InterlockedDecrement(&cRefCount_); 549 550 if (newCount == 0) 551 { 552 delete this; 553 return 0; 554 } 555 return newCount; 556 } 557 558 IFACEMETHOD(QueryInterface)( 559 IID const& riid, 560 void** ppvObject) 561 { 562 if (__uuidof(IDWriteTextRenderer) == riid) 563 { 564 *ppvObject = this; 565 } 566 else if (__uuidof(IDWritePixelSnapping) == riid) 567 { 568 *ppvObject = this; 569 } 570 else if (__uuidof(IUnknown) == riid) 571 { 572 *ppvObject = this; 573 } 574 else 575 { 576 *ppvObject = NULL; 577 return E_FAIL; 578 } 579 580 return S_OK; 581 } 582 583 private: 584 long cRefCount_; 585 DWriteContext* pDWC_; 586 }; 587 588 DWriteContext::DWriteContext() : 589 mHDC(NULL), 590 mBindRect(), 591 mDMode(DM_GDI), 592 mInteropHDC(NULL), 593 mDrawing(false), 594 mFallbackDC(false), 595 mD2D1Factory(NULL), 596 mRT(NULL), 597 mGDIRT(NULL), 598 mBrush(NULL), 599 mDWriteFactory(NULL), 600 #ifdef FEAT_DIRECTX_COLOR_EMOJI 601 mDWriteFactory2(NULL), 602 #endif 603 mGdiInterop(NULL), 604 mRenderingParams(NULL), 605 mFontCache(8), 606 mTextFormat(NULL), 607 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL), 608 mFontStyle(DWRITE_FONT_STYLE_NORMAL), 609 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT) 610 { 611 HRESULT hr; 612 613 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 614 __uuidof(ID2D1Factory), NULL, 615 reinterpret_cast<void**>(&mD2D1Factory)); 616 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory); 617 618 if (SUCCEEDED(hr)) 619 hr = CreateDeviceResources(); 620 621 if (SUCCEEDED(hr)) 622 { 623 hr = DWriteCreateFactory( 624 DWRITE_FACTORY_TYPE_SHARED, 625 __uuidof(IDWriteFactory), 626 reinterpret_cast<IUnknown**>(&mDWriteFactory)); 627 _RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr, 628 mDWriteFactory); 629 } 630 631 #ifdef FEAT_DIRECTX_COLOR_EMOJI 632 if (SUCCEEDED(hr)) 633 { 634 DWriteCreateFactory( 635 DWRITE_FACTORY_TYPE_SHARED, 636 __uuidof(IDWriteFactory2), 637 reinterpret_cast<IUnknown**>(&mDWriteFactory2)); 638 _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available"); 639 } 640 #endif 641 642 if (SUCCEEDED(hr)) 643 { 644 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop); 645 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop); 646 } 647 648 if (SUCCEEDED(hr)) 649 { 650 hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams); 651 _RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr, 652 mRenderingParams); 653 } 654 } 655 656 DWriteContext::~DWriteContext() 657 { 658 SafeRelease(&mTextFormat); 659 SafeRelease(&mRenderingParams); 660 SafeRelease(&mGdiInterop); 661 SafeRelease(&mDWriteFactory); 662 #ifdef FEAT_DIRECTX_COLOR_EMOJI 663 SafeRelease(&mDWriteFactory2); 664 #endif 665 SafeRelease(&mBrush); 666 SafeRelease(&mGDIRT); 667 SafeRelease(&mRT); 668 SafeRelease(&mD2D1Factory); 669 } 670 671 HRESULT 672 DWriteContext::CreateDeviceResources() 673 { 674 HRESULT hr; 675 676 if (mRT != NULL) 677 return S_OK; 678 679 D2D1_RENDER_TARGET_PROPERTIES props = { 680 D2D1_RENDER_TARGET_TYPE_DEFAULT, 681 { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE }, 682 0, 0, 683 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, 684 D2D1_FEATURE_LEVEL_DEFAULT 685 }; 686 hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT); 687 _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT); 688 689 if (SUCCEEDED(hr)) 690 { 691 // This always succeeds. 692 mRT->QueryInterface( 693 __uuidof(ID2D1GdiInteropRenderTarget), 694 reinterpret_cast<void**>(&mGDIRT)); 695 _RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT); 696 } 697 698 if (SUCCEEDED(hr)) 699 { 700 hr = mRT->CreateSolidColorBrush( 701 D2D1::ColorF(D2D1::ColorF::Black), 702 &mBrush); 703 _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush); 704 } 705 706 if (SUCCEEDED(hr)) 707 { 708 if (mHDC != NULL) 709 { 710 mRT->BindDC(mHDC, &mBindRect); 711 mRT->SetTransform(D2D1::IdentityMatrix()); 712 } 713 } 714 715 return hr; 716 } 717 718 void 719 DWriteContext::DiscardDeviceResources() 720 { 721 SafeRelease(&mBrush); 722 SafeRelease(&mGDIRT); 723 SafeRelease(&mRT); 724 } 725 726 HRESULT 727 DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, 728 IDWriteTextFormat **ppTextFormat) 729 { 730 // Most of this function is copied from: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/RenderTest/TextHelpers.cpp 731 HRESULT hr = S_OK; 732 IDWriteTextFormat *pTextFormat = NULL; 733 734 IDWriteFont *font = NULL; 735 IDWriteFontFamily *fontFamily = NULL; 736 IDWriteLocalizedStrings *localizedFamilyNames = NULL; 737 float fontSize = 0; 738 739 if (SUCCEEDED(hr)) 740 { 741 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font); 742 } 743 744 // Get the font family to which this font belongs. 745 if (SUCCEEDED(hr)) 746 { 747 hr = font->GetFontFamily(&fontFamily); 748 } 749 750 // Get the family names. This returns an object that encapsulates one or 751 // more names with the same meaning but in different languages. 752 if (SUCCEEDED(hr)) 753 { 754 hr = fontFamily->GetFamilyNames(&localizedFamilyNames); 755 } 756 757 // Get the family name at index zero. If we were going to display the name 758 // we'd want to try to find one that matched the use locale, but for 759 // purposes of creating a text format object any language will do. 760 761 wchar_t familyName[100]; 762 if (SUCCEEDED(hr)) 763 { 764 hr = localizedFamilyNames->GetString(0, familyName, 765 ARRAYSIZE(familyName)); 766 } 767 768 if (SUCCEEDED(hr)) 769 { 770 // Use lfHeight of the LOGFONT as font size. 771 fontSize = float(logFont.lfHeight); 772 773 if (fontSize < 0) 774 { 775 // Negative lfHeight represents the size of the em unit. 776 fontSize = -fontSize; 777 } 778 else 779 { 780 // Positive lfHeight represents the cell height (ascent + 781 // descent). 782 DWRITE_FONT_METRICS fontMetrics; 783 font->GetMetrics(&fontMetrics); 784 785 // Convert the cell height (ascent + descent) from design units 786 // to ems. 787 float cellHeight = static_cast<float>( 788 fontMetrics.ascent + fontMetrics.descent) 789 / fontMetrics.designUnitsPerEm; 790 791 // Divide the font size by the cell height to get the font em 792 // size. 793 fontSize /= cellHeight; 794 } 795 } 796 797 // The text format includes a locale name. Ideally, this would be the 798 // language of the text, which may or may not be the same as the primary 799 // language of the user. However, for our purposes the user locale will do. 800 wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; 801 if (SUCCEEDED(hr)) 802 { 803 if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0) 804 hr = HRESULT_FROM_WIN32(GetLastError()); 805 } 806 807 if (SUCCEEDED(hr)) 808 { 809 // Create the text format object. 810 hr = mDWriteFactory->CreateTextFormat( 811 familyName, 812 NULL, // no custom font collection 813 font->GetWeight(), 814 font->GetStyle(), 815 font->GetStretch(), 816 fontSize, 817 localeName, 818 &pTextFormat); 819 } 820 821 if (SUCCEEDED(hr)) 822 hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); 823 824 if (SUCCEEDED(hr)) 825 hr = pTextFormat->SetParagraphAlignment( 826 DWRITE_PARAGRAPH_ALIGNMENT_CENTER); 827 828 if (SUCCEEDED(hr)) 829 hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); 830 831 SafeRelease(&localizedFamilyNames); 832 SafeRelease(&fontFamily); 833 SafeRelease(&font); 834 835 if (SUCCEEDED(hr)) 836 *ppTextFormat = pTextFormat; 837 else 838 SafeRelease(&pTextFormat); 839 840 return hr; 841 } 842 843 HRESULT 844 DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont) 845 { 846 HRESULT hr = S_OK; 847 IDWriteTextFormat *pTextFormat = NULL; 848 849 hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat); 850 851 if (SUCCEEDED(hr)) 852 { 853 SafeRelease(&mTextFormat); 854 mTextFormat = pTextFormat; 855 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight); 856 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC 857 : DWRITE_FONT_STYLE_NORMAL; 858 } 859 860 return hr; 861 } 862 863 void 864 DWriteContext::SetFont(HFONT hFont) 865 { 866 FontCache::Item item; 867 if (mFontCache.get(hFont, item)) 868 { 869 if (item.pTextFormat != NULL) 870 { 871 item.pTextFormat->AddRef(); 872 SafeRelease(&mTextFormat); 873 mTextFormat = item.pTextFormat; 874 mFontWeight = item.fontWeight; 875 mFontStyle = item.fontStyle; 876 mFallbackDC = false; 877 } 878 else 879 mFallbackDC = true; 880 return; 881 } 882 883 HRESULT hr = E_FAIL; 884 LOGFONTW lf; 885 if (GetObjectW(hFont, sizeof(lf), &lf)) 886 hr = SetFontByLOGFONT(lf); 887 888 item.hFont = hFont; 889 if (SUCCEEDED(hr)) 890 { 891 item.pTextFormat = mTextFormat; 892 item.fontWeight = mFontWeight; 893 item.fontStyle = mFontStyle; 894 mFallbackDC = false; 895 } 896 else 897 mFallbackDC = true; 898 mFontCache.put(item); 899 } 900 901 void 902 DWriteContext::BindDC(HDC hdc, const RECT *rect) 903 { 904 Flush(); 905 mRT->BindDC(hdc, rect); 906 mRT->SetTransform(D2D1::IdentityMatrix()); 907 mHDC = hdc; 908 mBindRect = *rect; 909 } 910 911 HRESULT 912 DWriteContext::SetDrawingMode(DrawingMode mode) 913 { 914 HRESULT hr = S_OK; 915 916 switch (mode) 917 { 918 default: 919 case DM_GDI: 920 if (mInteropHDC != NULL) 921 { 922 mGDIRT->ReleaseDC(NULL); 923 mInteropHDC = NULL; 924 } 925 if (mDrawing) 926 { 927 hr = mRT->EndDraw(); 928 if (hr == D2DERR_RECREATE_TARGET) 929 { 930 hr = S_OK; 931 DiscardDeviceResources(); 932 CreateDeviceResources(); 933 } 934 mDrawing = false; 935 } 936 break; 937 938 case DM_DIRECTX: 939 if (mInteropHDC != NULL) 940 { 941 mGDIRT->ReleaseDC(NULL); 942 mInteropHDC = NULL; 943 } 944 else if (mDrawing == false) 945 { 946 CreateDeviceResources(); 947 mRT->BeginDraw(); 948 mDrawing = true; 949 } 950 break; 951 952 case DM_INTEROP: 953 if (mDrawing == false) 954 { 955 CreateDeviceResources(); 956 mRT->BeginDraw(); 957 mDrawing = true; 958 } 959 if (mInteropHDC == NULL) 960 hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC); 961 break; 962 } 963 mDMode = mode; 964 return hr; 965 } 966 967 ID2D1Brush* 968 DWriteContext::SolidBrush(COLORREF color) 969 { 970 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 | 971 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color)))); 972 return mBrush; 973 } 974 975 void 976 DWriteContext::DrawText(const WCHAR *text, int len, 977 int x, int y, int w, int h, int cellWidth, COLORREF color, 978 UINT fuOptions, const RECT *lprc, const INT *lpDx) 979 { 980 if (mFallbackDC) 981 { 982 // Fall back to GDI rendering. 983 HRESULT hr = SetDrawingMode(DM_INTEROP); 984 if (SUCCEEDED(hr)) 985 { 986 HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT); 987 HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont); 988 ::SetTextColor(mInteropHDC, color); 989 ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC)); 990 ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx); 991 ::SelectObject(mInteropHDC, hOldFont); 992 } 993 return; 994 } 995 996 HRESULT hr; 997 IDWriteTextLayout *textLayout = NULL; 998 999 SetDrawingMode(DM_DIRECTX); 1000 1001 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat, 1002 FLOAT(w), FLOAT(h), &textLayout); 1003 1004 if (SUCCEEDED(hr)) 1005 { 1006 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) }; 1007 textLayout->SetFontWeight(mFontWeight, textRange); 1008 textLayout->SetFontStyle(mFontStyle, textRange); 1009 1010 TextRenderer renderer(this); 1011 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f }; 1012 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y) - 0.5f); 1013 } 1014 1015 SafeRelease(&textLayout); 1016 } 1017 1018 void 1019 DWriteContext::FillRect(const RECT *rc, COLORREF color) 1020 { 1021 if (mDMode == DM_INTEROP) 1022 { 1023 // GDI functions are used before this call. Keep using GDI. 1024 // (Switching to Direct2D causes terrible slowdown.) 1025 HBRUSH hbr = ::CreateSolidBrush(color); 1026 ::FillRect(mInteropHDC, rc, hbr); 1027 ::DeleteObject(HGDIOBJ(hbr)); 1028 } 1029 else 1030 { 1031 SetDrawingMode(DM_DIRECTX); 1032 mRT->FillRectangle( 1033 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), 1034 FLOAT(rc->right), FLOAT(rc->bottom)), 1035 SolidBrush(color)); 1036 } 1037 } 1038 1039 void 1040 DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color) 1041 { 1042 if (mDMode == DM_INTEROP) 1043 { 1044 // GDI functions are used before this call. Keep using GDI. 1045 // (Switching to Direct2D causes terrible slowdown.) 1046 HPEN hpen = ::CreatePen(PS_SOLID, 1, color); 1047 HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen)); 1048 ::MoveToEx(mInteropHDC, x1, y1, NULL); 1049 ::LineTo(mInteropHDC, x2, y2); 1050 ::SelectObject(mInteropHDC, old_pen); 1051 ::DeleteObject(HGDIOBJ(hpen)); 1052 } 1053 else 1054 { 1055 SetDrawingMode(DM_DIRECTX); 1056 mRT->DrawLine( 1057 D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f), 1058 D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f), 1059 SolidBrush(color)); 1060 } 1061 } 1062 1063 void 1064 DWriteContext::SetPixel(int x, int y, COLORREF color) 1065 { 1066 if (mDMode == DM_INTEROP) 1067 { 1068 // GDI functions are used before this call. Keep using GDI. 1069 // (Switching to Direct2D causes terrible slowdown.) 1070 ::SetPixel(mInteropHDC, x, y, color); 1071 } 1072 else 1073 { 1074 SetDrawingMode(DM_DIRECTX); 1075 // Direct2D doesn't have SetPixel API. Use DrawLine instead. 1076 mRT->DrawLine( 1077 D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f), 1078 D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f), 1079 SolidBrush(color)); 1080 } 1081 } 1082 1083 void 1084 DWriteContext::Flush() 1085 { 1086 SetDrawingMode(DM_GDI); 1087 } 1088 1089 void 1090 DWriteContext::SetRenderingParams( 1091 const DWriteRenderingParams *params) 1092 { 1093 if (mDWriteFactory == NULL) 1094 return; 1095 1096 IDWriteRenderingParams *renderingParams = NULL; 1097 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode = 1098 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 1099 HRESULT hr; 1100 if (params != NULL) 1101 { 1102 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma, 1103 params->enhancedContrast, params->clearTypeLevel, 1104 ToPixelGeometry(params->pixelGeometry), 1105 ToRenderingMode(params->renderingMode), &renderingParams); 1106 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode); 1107 } 1108 else 1109 hr = mDWriteFactory->CreateRenderingParams(&renderingParams); 1110 if (SUCCEEDED(hr) && renderingParams != NULL) 1111 { 1112 SafeRelease(&mRenderingParams); 1113 mRenderingParams = renderingParams; 1114 mTextAntialiasMode = textAntialiasMode; 1115 1116 Flush(); 1117 mRT->SetTextRenderingParams(mRenderingParams); 1118 mRT->SetTextAntialiasMode(mTextAntialiasMode); 1119 } 1120 } 1121 1122 DWriteRenderingParams * 1123 DWriteContext::GetRenderingParams( 1124 DWriteRenderingParams *params) 1125 { 1126 if (params != NULL && mRenderingParams != NULL) 1127 { 1128 params->gamma = mRenderingParams->GetGamma(); 1129 params->enhancedContrast = mRenderingParams->GetEnhancedContrast(); 1130 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel(); 1131 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry()); 1132 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode()); 1133 params->textAntialiasMode = mTextAntialiasMode; 1134 } 1135 return params; 1136 } 1137 1138 //////////////////////////////////////////////////////////////////////////// 1139 // PUBLIC C INTERFACES 1140 1141 void 1142 DWrite_Init(void) 1143 { 1144 #ifdef DYNAMIC_DIRECTX 1145 // Load libraries. 1146 hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll")); 1147 hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll")); 1148 if (hD2D1DLL == NULL || hDWriteDLL == NULL) 1149 { 1150 DWrite_Final(); 1151 return; 1152 } 1153 // Get address of procedures. 1154 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress( 1155 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName"); 1156 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL, 1157 "D2D1CreateFactory"); 1158 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL, 1159 "DWriteCreateFactory"); 1160 #endif 1161 } 1162 1163 void 1164 DWrite_Final(void) 1165 { 1166 #ifdef DYNAMIC_DIRECTX 1167 pGetUserDefaultLocaleName = NULL; 1168 pD2D1CreateFactory = NULL; 1169 pDWriteCreateFactory = NULL; 1170 unload(hDWriteDLL); 1171 unload(hD2D1DLL); 1172 #endif 1173 } 1174 1175 DWriteContext * 1176 DWriteContext_Open(void) 1177 { 1178 #ifdef DYNAMIC_DIRECTX 1179 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL 1180 || pDWriteCreateFactory == NULL) 1181 return NULL; 1182 #endif 1183 return new DWriteContext(); 1184 } 1185 1186 void 1187 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect) 1188 { 1189 if (ctx != NULL) 1190 ctx->BindDC(hdc, rect); 1191 } 1192 1193 void 1194 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) 1195 { 1196 if (ctx != NULL) 1197 ctx->SetFont(hFont); 1198 } 1199 1200 void 1201 DWriteContext_DrawText( 1202 DWriteContext *ctx, 1203 const WCHAR *text, 1204 int len, 1205 int x, 1206 int y, 1207 int w, 1208 int h, 1209 int cellWidth, 1210 COLORREF color, 1211 UINT fuOptions, 1212 const RECT *lprc, 1213 const INT *lpDx) 1214 { 1215 if (ctx != NULL) 1216 ctx->DrawText(text, len, x, y, w, h, cellWidth, color, 1217 fuOptions, lprc, lpDx); 1218 } 1219 1220 void 1221 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color) 1222 { 1223 if (ctx != NULL) 1224 ctx->FillRect(rc, color); 1225 } 1226 1227 void 1228 DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2, 1229 COLORREF color) 1230 { 1231 if (ctx != NULL) 1232 ctx->DrawLine(x1, y1, x2, y2, color); 1233 } 1234 1235 void 1236 DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color) 1237 { 1238 if (ctx != NULL) 1239 ctx->SetPixel(x, y, color); 1240 } 1241 1242 void 1243 DWriteContext_Flush(DWriteContext *ctx) 1244 { 1245 if (ctx != NULL) 1246 ctx->Flush(); 1247 } 1248 1249 void 1250 DWriteContext_Close(DWriteContext *ctx) 1251 { 1252 delete ctx; 1253 } 1254 1255 void 1256 DWriteContext_SetRenderingParams( 1257 DWriteContext *ctx, 1258 const DWriteRenderingParams *params) 1259 { 1260 if (ctx != NULL) 1261 ctx->SetRenderingParams(params); 1262 } 1263 1264 DWriteRenderingParams * 1265 DWriteContext_GetRenderingParams( 1266 DWriteContext *ctx, 1267 DWriteRenderingParams *params) 1268 { 1269 if (ctx != NULL) 1270 return ctx->GetRenderingParams(params); 1271 else 1272 return NULL; 1273 } 1274