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_CENTER); 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 HRESULT 934 DWriteContext::SetDrawingMode(DrawingMode mode) 935 { 936 HRESULT hr = S_OK; 937 938 switch (mode) 939 { 940 default: 941 case DM_GDI: 942 if (mInteropHDC != NULL) 943 { 944 mGDIRT->ReleaseDC(NULL); 945 mInteropHDC = NULL; 946 } 947 if (mDrawing) 948 { 949 hr = mRT->EndDraw(); 950 if (hr == D2DERR_RECREATE_TARGET) 951 { 952 hr = S_OK; 953 DiscardDeviceResources(); 954 CreateDeviceResources(); 955 } 956 mDrawing = false; 957 } 958 break; 959 960 case DM_DIRECTX: 961 if (mInteropHDC != NULL) 962 { 963 mGDIRT->ReleaseDC(NULL); 964 mInteropHDC = NULL; 965 } 966 else if (mDrawing == false) 967 { 968 CreateDeviceResources(); 969 mRT->BeginDraw(); 970 mDrawing = true; 971 } 972 break; 973 974 case DM_INTEROP: 975 if (mDrawing == false) 976 { 977 CreateDeviceResources(); 978 mRT->BeginDraw(); 979 mDrawing = true; 980 } 981 if (mInteropHDC == NULL) 982 hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC); 983 break; 984 } 985 mDMode = mode; 986 return hr; 987 } 988 989 ID2D1Brush* 990 DWriteContext::SolidBrush(COLORREF color) 991 { 992 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 | 993 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color)))); 994 return mBrush; 995 } 996 997 void 998 DWriteContext::DrawText(const WCHAR *text, int len, 999 int x, int y, int w, int h, int cellWidth, COLORREF color, 1000 UINT fuOptions, const RECT *lprc, const INT *lpDx) 1001 { 1002 if (mFallbackDC) 1003 { 1004 // Fall back to GDI rendering. 1005 HRESULT hr = SetDrawingMode(DM_INTEROP); 1006 if (SUCCEEDED(hr)) 1007 { 1008 HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT); 1009 HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont); 1010 ::SetTextColor(mInteropHDC, color); 1011 ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC)); 1012 ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx); 1013 ::SelectObject(mInteropHDC, hOldFont); 1014 } 1015 return; 1016 } 1017 1018 HRESULT hr; 1019 IDWriteTextLayout *textLayout = NULL; 1020 1021 SetDrawingMode(DM_DIRECTX); 1022 1023 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat, 1024 FLOAT(w), FLOAT(h), &textLayout); 1025 1026 if (SUCCEEDED(hr)) 1027 { 1028 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) }; 1029 textLayout->SetFontWeight(mFontWeight, textRange); 1030 textLayout->SetFontStyle(mFontStyle, textRange); 1031 1032 TextRenderer renderer(this); 1033 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f }; 1034 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y) - 0.5f); 1035 } 1036 1037 SafeRelease(&textLayout); 1038 } 1039 1040 void 1041 DWriteContext::FillRect(const RECT *rc, COLORREF color) 1042 { 1043 if (mDMode == DM_INTEROP) 1044 { 1045 // GDI functions are used before this call. Keep using GDI. 1046 // (Switching to Direct2D causes terrible slowdown.) 1047 HBRUSH hbr = ::CreateSolidBrush(color); 1048 ::FillRect(mInteropHDC, rc, hbr); 1049 ::DeleteObject(HGDIOBJ(hbr)); 1050 } 1051 else 1052 { 1053 SetDrawingMode(DM_DIRECTX); 1054 mRT->FillRectangle( 1055 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), 1056 FLOAT(rc->right), FLOAT(rc->bottom)), 1057 SolidBrush(color)); 1058 } 1059 } 1060 1061 void 1062 DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color) 1063 { 1064 if (mDMode == DM_INTEROP) 1065 { 1066 // GDI functions are used before this call. Keep using GDI. 1067 // (Switching to Direct2D causes terrible slowdown.) 1068 HPEN hpen = ::CreatePen(PS_SOLID, 1, color); 1069 HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen)); 1070 ::MoveToEx(mInteropHDC, x1, y1, NULL); 1071 ::LineTo(mInteropHDC, x2, y2); 1072 ::SelectObject(mInteropHDC, old_pen); 1073 ::DeleteObject(HGDIOBJ(hpen)); 1074 } 1075 else 1076 { 1077 SetDrawingMode(DM_DIRECTX); 1078 mRT->DrawLine( 1079 D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f), 1080 D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f), 1081 SolidBrush(color)); 1082 } 1083 } 1084 1085 void 1086 DWriteContext::SetPixel(int x, int y, COLORREF color) 1087 { 1088 if (mDMode == DM_INTEROP) 1089 { 1090 // GDI functions are used before this call. Keep using GDI. 1091 // (Switching to Direct2D causes terrible slowdown.) 1092 ::SetPixel(mInteropHDC, x, y, color); 1093 } 1094 else 1095 { 1096 SetDrawingMode(DM_DIRECTX); 1097 // Direct2D doesn't have SetPixel API. Use DrawLine instead. 1098 mRT->DrawLine( 1099 D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f), 1100 D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f), 1101 SolidBrush(color)); 1102 } 1103 } 1104 1105 void 1106 DWriteContext::Scroll(int x, int y, const RECT *rc) 1107 { 1108 SetDrawingMode(DM_DIRECTX); 1109 mRT->Flush(); 1110 1111 D2D1_RECT_U srcRect; 1112 D2D1_POINT_2U destPoint; 1113 if (x >= 0) 1114 { 1115 srcRect.left = rc->left; 1116 srcRect.right = rc->right - x; 1117 destPoint.x = rc->left + x; 1118 } 1119 else 1120 { 1121 srcRect.left = rc->left - x; 1122 srcRect.right = rc->right; 1123 destPoint.x = rc->left; 1124 } 1125 if (y >= 0) 1126 { 1127 srcRect.top = rc->top; 1128 srcRect.bottom = rc->bottom - y; 1129 destPoint.y = rc->top + y; 1130 } 1131 else 1132 { 1133 srcRect.top = rc->top - y; 1134 srcRect.bottom = rc->bottom; 1135 destPoint.y = rc->top; 1136 } 1137 mBitmap->CopyFromRenderTarget(&destPoint, mRT, &srcRect); 1138 1139 D2D1_RECT_F destRect = { 1140 FLOAT(destPoint.x), FLOAT(destPoint.y), 1141 FLOAT(destPoint.x + srcRect.right - srcRect.left), 1142 FLOAT(destPoint.y + srcRect.bottom - srcRect.top) 1143 }; 1144 mRT->DrawBitmap(mBitmap, destRect, 1.0F, 1145 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, destRect); 1146 } 1147 1148 void 1149 DWriteContext::Flush() 1150 { 1151 SetDrawingMode(DM_GDI); 1152 } 1153 1154 void 1155 DWriteContext::SetRenderingParams( 1156 const DWriteRenderingParams *params) 1157 { 1158 if (mDWriteFactory == NULL) 1159 return; 1160 1161 IDWriteRenderingParams *renderingParams = NULL; 1162 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode = 1163 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 1164 HRESULT hr; 1165 if (params != NULL) 1166 { 1167 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma, 1168 params->enhancedContrast, params->clearTypeLevel, 1169 ToPixelGeometry(params->pixelGeometry), 1170 ToRenderingMode(params->renderingMode), &renderingParams); 1171 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode); 1172 } 1173 else 1174 hr = mDWriteFactory->CreateRenderingParams(&renderingParams); 1175 if (SUCCEEDED(hr) && renderingParams != NULL) 1176 { 1177 SafeRelease(&mRenderingParams); 1178 mRenderingParams = renderingParams; 1179 mTextAntialiasMode = textAntialiasMode; 1180 1181 Flush(); 1182 mRT->SetTextRenderingParams(mRenderingParams); 1183 mRT->SetTextAntialiasMode(mTextAntialiasMode); 1184 } 1185 } 1186 1187 DWriteRenderingParams * 1188 DWriteContext::GetRenderingParams( 1189 DWriteRenderingParams *params) 1190 { 1191 if (params != NULL && mRenderingParams != NULL) 1192 { 1193 params->gamma = mRenderingParams->GetGamma(); 1194 params->enhancedContrast = mRenderingParams->GetEnhancedContrast(); 1195 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel(); 1196 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry()); 1197 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode()); 1198 params->textAntialiasMode = mTextAntialiasMode; 1199 } 1200 return params; 1201 } 1202 1203 //////////////////////////////////////////////////////////////////////////// 1204 // PUBLIC C INTERFACES 1205 1206 void 1207 DWrite_Init(void) 1208 { 1209 #ifdef DYNAMIC_DIRECTX 1210 // Load libraries. 1211 hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll")); 1212 hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll")); 1213 if (hD2D1DLL == NULL || hDWriteDLL == NULL) 1214 { 1215 DWrite_Final(); 1216 return; 1217 } 1218 // Get address of procedures. 1219 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress( 1220 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName"); 1221 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL, 1222 "D2D1CreateFactory"); 1223 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL, 1224 "DWriteCreateFactory"); 1225 #endif 1226 } 1227 1228 void 1229 DWrite_Final(void) 1230 { 1231 #ifdef DYNAMIC_DIRECTX 1232 pGetUserDefaultLocaleName = NULL; 1233 pD2D1CreateFactory = NULL; 1234 pDWriteCreateFactory = NULL; 1235 unload(hDWriteDLL); 1236 unload(hD2D1DLL); 1237 #endif 1238 } 1239 1240 DWriteContext * 1241 DWriteContext_Open(void) 1242 { 1243 #ifdef DYNAMIC_DIRECTX 1244 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL 1245 || pDWriteCreateFactory == NULL) 1246 return NULL; 1247 #endif 1248 return new DWriteContext(); 1249 } 1250 1251 void 1252 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect) 1253 { 1254 if (ctx != NULL) 1255 ctx->BindDC(hdc, rect); 1256 } 1257 1258 void 1259 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) 1260 { 1261 if (ctx != NULL) 1262 ctx->SetFont(hFont); 1263 } 1264 1265 void 1266 DWriteContext_DrawText( 1267 DWriteContext *ctx, 1268 const WCHAR *text, 1269 int len, 1270 int x, 1271 int y, 1272 int w, 1273 int h, 1274 int cellWidth, 1275 COLORREF color, 1276 UINT fuOptions, 1277 const RECT *lprc, 1278 const INT *lpDx) 1279 { 1280 if (ctx != NULL) 1281 ctx->DrawText(text, len, x, y, w, h, cellWidth, color, 1282 fuOptions, lprc, lpDx); 1283 } 1284 1285 void 1286 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color) 1287 { 1288 if (ctx != NULL) 1289 ctx->FillRect(rc, color); 1290 } 1291 1292 void 1293 DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2, 1294 COLORREF color) 1295 { 1296 if (ctx != NULL) 1297 ctx->DrawLine(x1, y1, x2, y2, color); 1298 } 1299 1300 void 1301 DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color) 1302 { 1303 if (ctx != NULL) 1304 ctx->SetPixel(x, y, color); 1305 } 1306 1307 void 1308 DWriteContext_Scroll(DWriteContext *ctx, int x, int y, const RECT *rc) 1309 { 1310 if (ctx != NULL) 1311 ctx->Scroll(x, y, rc); 1312 } 1313 1314 void 1315 DWriteContext_Flush(DWriteContext *ctx) 1316 { 1317 if (ctx != NULL) 1318 ctx->Flush(); 1319 } 1320 1321 void 1322 DWriteContext_Close(DWriteContext *ctx) 1323 { 1324 delete ctx; 1325 } 1326 1327 void 1328 DWriteContext_SetRenderingParams( 1329 DWriteContext *ctx, 1330 const DWriteRenderingParams *params) 1331 { 1332 if (ctx != NULL) 1333 ctx->SetRenderingParams(params); 1334 } 1335 1336 DWriteRenderingParams * 1337 DWriteContext_GetRenderingParams( 1338 DWriteContext *ctx, 1339 DWriteRenderingParams *params) 1340 { 1341 if (ctx != NULL) 1342 return ctx->GetRenderingParams(params); 1343 else 1344 return NULL; 1345 } 1346