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 if (item.pTextFormat != NULL) 243 item.pTextFormat->AddRef(); 244 } 245 mItems[n] = item; 246 slide(n); 247 } 248 249 private: 250 int find(HFONT hFont) 251 { 252 for (int i = 0; i < mSize; ++i) 253 { 254 if (mItems[i].hFont == hFont) 255 return i; 256 } 257 return -1; 258 } 259 260 void slide(int nextTop) 261 { 262 if (nextTop == 0) 263 return; 264 Item tmp = mItems[nextTop]; 265 for (int i = nextTop - 1; i >= 0; --i) 266 mItems[i + 1] = mItems[i]; 267 mItems[0] = tmp; 268 } 269 }; 270 271 enum DrawingMode { 272 DM_GDI = 0, 273 DM_DIRECTX = 1, 274 DM_INTEROP = 2, 275 }; 276 277 struct DWriteContext { 278 HDC mHDC; 279 RECT mBindRect; 280 DrawingMode mDMode; 281 HDC mInteropHDC; 282 bool mDrawing; 283 bool mFallbackDC; 284 285 ID2D1Factory *mD2D1Factory; 286 287 ID2D1DCRenderTarget *mRT; 288 ID2D1GdiInteropRenderTarget *mGDIRT; 289 ID2D1SolidColorBrush *mBrush; 290 ID2D1Bitmap *mBitmap; 291 292 IDWriteFactory *mDWriteFactory; 293 #ifdef FEAT_DIRECTX_COLOR_EMOJI 294 IDWriteFactory2 *mDWriteFactory2; 295 #endif 296 297 IDWriteGdiInterop *mGdiInterop; 298 IDWriteRenderingParams *mRenderingParams; 299 300 FontCache mFontCache; 301 IDWriteTextFormat *mTextFormat; 302 DWRITE_FONT_WEIGHT mFontWeight; 303 DWRITE_FONT_STYLE mFontStyle; 304 305 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode; 306 307 // METHODS 308 309 DWriteContext(); 310 311 virtual ~DWriteContext(); 312 313 HRESULT CreateDeviceResources(); 314 315 void DiscardDeviceResources(); 316 317 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, 318 IDWriteTextFormat **ppTextFormat); 319 320 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont); 321 322 void SetFont(HFONT hFont); 323 324 void Rebind(); 325 326 void BindDC(HDC hdc, const RECT *rect); 327 328 HRESULT SetDrawingMode(DrawingMode mode); 329 330 ID2D1Brush* SolidBrush(COLORREF color); 331 332 void DrawText(const WCHAR *text, int len, 333 int x, int y, int w, int h, int cellWidth, COLORREF color, 334 UINT fuOptions, const RECT *lprc, const INT *lpDx); 335 336 void FillRect(const RECT *rc, COLORREF color); 337 338 void DrawLine(int x1, int y1, int x2, int y2, COLORREF color); 339 340 void SetPixel(int x, int y, COLORREF color); 341 342 void Scroll(int x, int y, const RECT *rc); 343 344 void Flush(); 345 346 void SetRenderingParams( 347 const DWriteRenderingParams *params); 348 349 DWriteRenderingParams *GetRenderingParams( 350 DWriteRenderingParams *params); 351 }; 352 353 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN 354 { 355 private: 356 FLOAT &mAccum; 357 FLOAT mDelta; 358 FLOAT *mAdjustedAdvances; 359 360 public: 361 AdjustedGlyphRun( 362 const DWRITE_GLYPH_RUN *glyphRun, 363 FLOAT cellWidth, 364 FLOAT &accum) : 365 DWRITE_GLYPH_RUN(*glyphRun), 366 mAccum(accum), 367 mDelta(0.0f), 368 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]) 369 { 370 assert(cellWidth != 0.0f); 371 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i) 372 { 373 FLOAT orig = glyphRun->glyphAdvances[i]; 374 FLOAT adjusted = adjustToCell(orig, cellWidth); 375 mAdjustedAdvances[i] = adjusted; 376 mDelta += adjusted - orig; 377 } 378 glyphAdvances = mAdjustedAdvances; 379 } 380 381 ~AdjustedGlyphRun() 382 { 383 mAccum += mDelta; 384 delete[] mAdjustedAdvances; 385 } 386 387 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth) 388 { 389 int cellCount = int(floor(value / cellWidth + 0.5f)); 390 if (cellCount < 1) 391 cellCount = 1; 392 return cellCount * cellWidth; 393 } 394 }; 395 396 struct TextRendererContext { 397 // const fields. 398 COLORREF color; 399 FLOAT cellWidth; 400 401 // working fields. 402 FLOAT offsetX; 403 }; 404 405 class TextRenderer FINAL : public IDWriteTextRenderer 406 { 407 public: 408 TextRenderer( 409 DWriteContext* pDWC) : 410 cRefCount_(0), 411 pDWC_(pDWC) 412 { 413 AddRef(); 414 } 415 416 // add "virtual" to avoid a compiler warning 417 virtual ~TextRenderer() 418 { 419 } 420 421 IFACEMETHOD(IsPixelSnappingDisabled)( 422 __maybenull void* clientDrawingContext, 423 __out BOOL* isDisabled) 424 { 425 *isDisabled = FALSE; 426 return S_OK; 427 } 428 429 IFACEMETHOD(GetCurrentTransform)( 430 __maybenull void* clientDrawingContext, 431 __out DWRITE_MATRIX* transform) 432 { 433 // forward the render target's transform 434 pDWC_->mRT->GetTransform( 435 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform)); 436 return S_OK; 437 } 438 439 IFACEMETHOD(GetPixelsPerDip)( 440 __maybenull void* clientDrawingContext, 441 __out FLOAT* pixelsPerDip) 442 { 443 float dpiX, unused; 444 pDWC_->mRT->GetDpi(&dpiX, &unused); 445 *pixelsPerDip = dpiX / 96.0f; 446 return S_OK; 447 } 448 449 IFACEMETHOD(DrawUnderline)( 450 __maybenull void* clientDrawingContext, 451 FLOAT baselineOriginX, 452 FLOAT baselineOriginY, 453 __in DWRITE_UNDERLINE const* underline, 454 IUnknown* clientDrawingEffect) 455 { 456 return E_NOTIMPL; 457 } 458 459 IFACEMETHOD(DrawStrikethrough)( 460 __maybenull void* clientDrawingContext, 461 FLOAT baselineOriginX, 462 FLOAT baselineOriginY, 463 __in DWRITE_STRIKETHROUGH const* strikethrough, 464 IUnknown* clientDrawingEffect) 465 { 466 return E_NOTIMPL; 467 } 468 469 IFACEMETHOD(DrawInlineObject)( 470 __maybenull void* clientDrawingContext, 471 FLOAT originX, 472 FLOAT originY, 473 IDWriteInlineObject* inlineObject, 474 BOOL isSideways, 475 BOOL isRightToLeft, 476 IUnknown* clientDrawingEffect) 477 { 478 return E_NOTIMPL; 479 } 480 481 IFACEMETHOD(DrawGlyphRun)( 482 __maybenull void* clientDrawingContext, 483 FLOAT baselineOriginX, 484 FLOAT baselineOriginY, 485 DWRITE_MEASURING_MODE measuringMode, 486 __in DWRITE_GLYPH_RUN const* glyphRun, 487 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 488 IUnknown* clientDrawingEffect) 489 { 490 TextRendererContext *context = 491 reinterpret_cast<TextRendererContext*>(clientDrawingContext); 492 493 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth, 494 context->offsetX); 495 496 #ifdef FEAT_DIRECTX_COLOR_EMOJI 497 if (pDWC_->mDWriteFactory2 != NULL) 498 { 499 IDWriteColorGlyphRunEnumerator *enumerator = NULL; 500 HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun( 501 baselineOriginX + context->offsetX, 502 baselineOriginY, 503 &adjustedGlyphRun, 504 NULL, 505 DWRITE_MEASURING_MODE_GDI_NATURAL, 506 NULL, 507 0, 508 &enumerator); 509 if (SUCCEEDED(hr)) 510 { 511 // Draw by IDWriteFactory2 for color emoji 512 BOOL hasRun = TRUE; 513 enumerator->MoveNext(&hasRun); 514 while (hasRun) 515 { 516 const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun; 517 enumerator->GetCurrentRun(&colorGlyphRun); 518 519 pDWC_->mBrush->SetColor(colorGlyphRun->runColor); 520 pDWC_->mRT->DrawGlyphRun( 521 D2D1::Point2F( 522 colorGlyphRun->baselineOriginX, 523 colorGlyphRun->baselineOriginY), 524 &colorGlyphRun->glyphRun, 525 pDWC_->mBrush, 526 DWRITE_MEASURING_MODE_NATURAL); 527 enumerator->MoveNext(&hasRun); 528 } 529 SafeRelease(&enumerator); 530 return S_OK; 531 } 532 } 533 #endif 534 535 // Draw by IDWriteFactory (without color emoji) 536 pDWC_->mRT->DrawGlyphRun( 537 D2D1::Point2F( 538 baselineOriginX + context->offsetX, 539 baselineOriginY), 540 &adjustedGlyphRun, 541 pDWC_->SolidBrush(context->color), 542 DWRITE_MEASURING_MODE_NATURAL); 543 return S_OK; 544 } 545 546 public: 547 IFACEMETHOD_(unsigned long, AddRef) () 548 { 549 return InterlockedIncrement(&cRefCount_); 550 } 551 552 IFACEMETHOD_(unsigned long, Release) () 553 { 554 long newCount = InterlockedDecrement(&cRefCount_); 555 556 if (newCount == 0) 557 { 558 delete this; 559 return 0; 560 } 561 return newCount; 562 } 563 564 IFACEMETHOD(QueryInterface)( 565 IID const& riid, 566 void** ppvObject) 567 { 568 if (__uuidof(IDWriteTextRenderer) == riid) 569 { 570 *ppvObject = this; 571 } 572 else if (__uuidof(IDWritePixelSnapping) == riid) 573 { 574 *ppvObject = this; 575 } 576 else if (__uuidof(IUnknown) == riid) 577 { 578 *ppvObject = this; 579 } 580 else 581 { 582 *ppvObject = NULL; 583 return E_FAIL; 584 } 585 586 return S_OK; 587 } 588 589 private: 590 long cRefCount_; 591 DWriteContext* pDWC_; 592 }; 593 594 DWriteContext::DWriteContext() : 595 mHDC(NULL), 596 mBindRect(), 597 mDMode(DM_GDI), 598 mInteropHDC(NULL), 599 mDrawing(false), 600 mFallbackDC(false), 601 mD2D1Factory(NULL), 602 mRT(NULL), 603 mGDIRT(NULL), 604 mBrush(NULL), 605 mBitmap(NULL), 606 mDWriteFactory(NULL), 607 #ifdef FEAT_DIRECTX_COLOR_EMOJI 608 mDWriteFactory2(NULL), 609 #endif 610 mGdiInterop(NULL), 611 mRenderingParams(NULL), 612 mFontCache(8), 613 mTextFormat(NULL), 614 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL), 615 mFontStyle(DWRITE_FONT_STYLE_NORMAL), 616 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT) 617 { 618 HRESULT hr; 619 620 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 621 __uuidof(ID2D1Factory), NULL, 622 reinterpret_cast<void**>(&mD2D1Factory)); 623 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory); 624 625 if (SUCCEEDED(hr)) 626 { 627 hr = DWriteCreateFactory( 628 DWRITE_FACTORY_TYPE_SHARED, 629 __uuidof(IDWriteFactory), 630 reinterpret_cast<IUnknown**>(&mDWriteFactory)); 631 _RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr, 632 mDWriteFactory); 633 } 634 635 #ifdef FEAT_DIRECTX_COLOR_EMOJI 636 if (SUCCEEDED(hr)) 637 { 638 DWriteCreateFactory( 639 DWRITE_FACTORY_TYPE_SHARED, 640 __uuidof(IDWriteFactory2), 641 reinterpret_cast<IUnknown**>(&mDWriteFactory2)); 642 _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available"); 643 } 644 #endif 645 646 if (SUCCEEDED(hr)) 647 { 648 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop); 649 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop); 650 } 651 652 if (SUCCEEDED(hr)) 653 { 654 hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams); 655 _RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr, 656 mRenderingParams); 657 } 658 } 659 660 DWriteContext::~DWriteContext() 661 { 662 SafeRelease(&mTextFormat); 663 SafeRelease(&mRenderingParams); 664 SafeRelease(&mGdiInterop); 665 SafeRelease(&mDWriteFactory); 666 #ifdef FEAT_DIRECTX_COLOR_EMOJI 667 SafeRelease(&mDWriteFactory2); 668 #endif 669 SafeRelease(&mBitmap); 670 SafeRelease(&mBrush); 671 SafeRelease(&mGDIRT); 672 SafeRelease(&mRT); 673 SafeRelease(&mD2D1Factory); 674 } 675 676 HRESULT 677 DWriteContext::CreateDeviceResources() 678 { 679 HRESULT hr; 680 681 if (mRT != NULL) 682 return S_OK; 683 684 D2D1_RENDER_TARGET_PROPERTIES props = { 685 D2D1_RENDER_TARGET_TYPE_DEFAULT, 686 { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE }, 687 0, 0, 688 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, 689 D2D1_FEATURE_LEVEL_DEFAULT 690 }; 691 hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT); 692 _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT); 693 694 if (SUCCEEDED(hr)) 695 { 696 // This always succeeds. 697 mRT->QueryInterface( 698 __uuidof(ID2D1GdiInteropRenderTarget), 699 reinterpret_cast<void**>(&mGDIRT)); 700 _RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT); 701 } 702 703 if (SUCCEEDED(hr)) 704 { 705 hr = mRT->CreateSolidColorBrush( 706 D2D1::ColorF(D2D1::ColorF::Black), 707 &mBrush); 708 _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush); 709 } 710 711 if (SUCCEEDED(hr)) 712 Rebind(); 713 714 return hr; 715 } 716 717 void 718 DWriteContext::DiscardDeviceResources() 719 { 720 SafeRelease(&mBitmap); 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_FAR); 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::Rebind() 903 { 904 SafeRelease(&mBitmap); 905 906 mRT->BindDC(mHDC, &mBindRect); 907 mRT->SetTransform(D2D1::IdentityMatrix()); 908 909 D2D1_BITMAP_PROPERTIES props = { 910 {DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE}, 911 96.0f, 96.0f 912 }; 913 mRT->CreateBitmap( 914 D2D1::SizeU(mBindRect.right - mBindRect.left, 915 mBindRect.bottom - mBindRect.top), 916 props, &mBitmap); 917 } 918 919 void 920 DWriteContext::BindDC(HDC hdc, const RECT *rect) 921 { 922 mHDC = hdc; 923 mBindRect = *rect; 924 925 if (mRT == NULL) 926 CreateDeviceResources(); 927 else 928 { 929 Flush(); 930 Rebind(); 931 } 932 } 933 934 extern "C" void redraw_later_clear(void); 935 936 HRESULT 937 DWriteContext::SetDrawingMode(DrawingMode mode) 938 { 939 HRESULT hr = S_OK; 940 941 switch (mode) 942 { 943 default: 944 case DM_GDI: 945 if (mInteropHDC != NULL) 946 { 947 mGDIRT->ReleaseDC(NULL); 948 mInteropHDC = NULL; 949 } 950 if (mDrawing) 951 { 952 hr = mRT->EndDraw(); 953 if (hr == (HRESULT)D2DERR_RECREATE_TARGET) 954 { 955 hr = S_OK; 956 DiscardDeviceResources(); 957 CreateDeviceResources(); 958 redraw_later_clear(); 959 } 960 mDrawing = false; 961 } 962 break; 963 964 case DM_DIRECTX: 965 if (mInteropHDC != NULL) 966 { 967 mGDIRT->ReleaseDC(NULL); 968 mInteropHDC = NULL; 969 } 970 else if (mDrawing == false) 971 { 972 CreateDeviceResources(); 973 mRT->BeginDraw(); 974 mDrawing = true; 975 } 976 break; 977 978 case DM_INTEROP: 979 if (mDrawing == false) 980 { 981 CreateDeviceResources(); 982 mRT->BeginDraw(); 983 mDrawing = true; 984 } 985 if (mInteropHDC == NULL) 986 hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC); 987 break; 988 } 989 mDMode = mode; 990 return hr; 991 } 992 993 ID2D1Brush* 994 DWriteContext::SolidBrush(COLORREF color) 995 { 996 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 | 997 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color)))); 998 return mBrush; 999 } 1000 1001 void 1002 DWriteContext::DrawText(const WCHAR *text, int len, 1003 int x, int y, int w, int h, int cellWidth, COLORREF color, 1004 UINT fuOptions, const RECT *lprc, const INT *lpDx) 1005 { 1006 if (mFallbackDC) 1007 { 1008 // Fall back to GDI rendering. 1009 HRESULT hr = SetDrawingMode(DM_INTEROP); 1010 if (SUCCEEDED(hr)) 1011 { 1012 HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT); 1013 HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont); 1014 ::SetTextColor(mInteropHDC, color); 1015 ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC)); 1016 ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx); 1017 ::SelectObject(mInteropHDC, hOldFont); 1018 } 1019 return; 1020 } 1021 1022 HRESULT hr; 1023 IDWriteTextLayout *textLayout = NULL; 1024 1025 SetDrawingMode(DM_DIRECTX); 1026 1027 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat, 1028 FLOAT(w), FLOAT(h), &textLayout); 1029 1030 if (SUCCEEDED(hr)) 1031 { 1032 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) }; 1033 textLayout->SetFontWeight(mFontWeight, textRange); 1034 textLayout->SetFontStyle(mFontStyle, textRange); 1035 1036 TextRenderer renderer(this); 1037 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f }; 1038 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y)); 1039 } 1040 1041 SafeRelease(&textLayout); 1042 } 1043 1044 void 1045 DWriteContext::FillRect(const RECT *rc, COLORREF color) 1046 { 1047 if (mDMode == DM_INTEROP) 1048 { 1049 // GDI functions are used before this call. Keep using GDI. 1050 // (Switching to Direct2D causes terrible slowdown.) 1051 HBRUSH hbr = ::CreateSolidBrush(color); 1052 ::FillRect(mInteropHDC, rc, hbr); 1053 ::DeleteObject(HGDIOBJ(hbr)); 1054 } 1055 else 1056 { 1057 SetDrawingMode(DM_DIRECTX); 1058 mRT->FillRectangle( 1059 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), 1060 FLOAT(rc->right), FLOAT(rc->bottom)), 1061 SolidBrush(color)); 1062 } 1063 } 1064 1065 void 1066 DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color) 1067 { 1068 if (mDMode == DM_INTEROP) 1069 { 1070 // GDI functions are used before this call. Keep using GDI. 1071 // (Switching to Direct2D causes terrible slowdown.) 1072 HPEN hpen = ::CreatePen(PS_SOLID, 1, color); 1073 HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen)); 1074 ::MoveToEx(mInteropHDC, x1, y1, NULL); 1075 ::LineTo(mInteropHDC, x2, y2); 1076 ::SelectObject(mInteropHDC, old_pen); 1077 ::DeleteObject(HGDIOBJ(hpen)); 1078 } 1079 else 1080 { 1081 SetDrawingMode(DM_DIRECTX); 1082 mRT->DrawLine( 1083 D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f), 1084 D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f), 1085 SolidBrush(color)); 1086 } 1087 } 1088 1089 void 1090 DWriteContext::SetPixel(int x, int y, COLORREF color) 1091 { 1092 if (mDMode == DM_INTEROP) 1093 { 1094 // GDI functions are used before this call. Keep using GDI. 1095 // (Switching to Direct2D causes terrible slowdown.) 1096 ::SetPixel(mInteropHDC, x, y, color); 1097 } 1098 else 1099 { 1100 SetDrawingMode(DM_DIRECTX); 1101 // Direct2D doesn't have SetPixel API. Use DrawLine instead. 1102 mRT->DrawLine( 1103 D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f), 1104 D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f), 1105 SolidBrush(color)); 1106 } 1107 } 1108 1109 void 1110 DWriteContext::Scroll(int x, int y, const RECT *rc) 1111 { 1112 SetDrawingMode(DM_DIRECTX); 1113 mRT->Flush(); 1114 1115 D2D1_RECT_U srcRect; 1116 D2D1_POINT_2U destPoint; 1117 if (x >= 0) 1118 { 1119 srcRect.left = rc->left; 1120 srcRect.right = rc->right - x; 1121 destPoint.x = rc->left + x; 1122 } 1123 else 1124 { 1125 srcRect.left = rc->left - x; 1126 srcRect.right = rc->right; 1127 destPoint.x = rc->left; 1128 } 1129 if (y >= 0) 1130 { 1131 srcRect.top = rc->top; 1132 srcRect.bottom = rc->bottom - y; 1133 destPoint.y = rc->top + y; 1134 } 1135 else 1136 { 1137 srcRect.top = rc->top - y; 1138 srcRect.bottom = rc->bottom; 1139 destPoint.y = rc->top; 1140 } 1141 mBitmap->CopyFromRenderTarget(&destPoint, mRT, &srcRect); 1142 1143 D2D1_RECT_F destRect = { 1144 FLOAT(destPoint.x), FLOAT(destPoint.y), 1145 FLOAT(destPoint.x + srcRect.right - srcRect.left), 1146 FLOAT(destPoint.y + srcRect.bottom - srcRect.top) 1147 }; 1148 mRT->DrawBitmap(mBitmap, destRect, 1.0F, 1149 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, destRect); 1150 } 1151 1152 void 1153 DWriteContext::Flush() 1154 { 1155 SetDrawingMode(DM_GDI); 1156 } 1157 1158 void 1159 DWriteContext::SetRenderingParams( 1160 const DWriteRenderingParams *params) 1161 { 1162 if (mDWriteFactory == NULL) 1163 return; 1164 1165 IDWriteRenderingParams *renderingParams = NULL; 1166 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode = 1167 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; 1168 HRESULT hr; 1169 if (params != NULL) 1170 { 1171 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma, 1172 params->enhancedContrast, params->clearTypeLevel, 1173 ToPixelGeometry(params->pixelGeometry), 1174 ToRenderingMode(params->renderingMode), &renderingParams); 1175 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode); 1176 } 1177 else 1178 hr = mDWriteFactory->CreateRenderingParams(&renderingParams); 1179 if (SUCCEEDED(hr) && renderingParams != NULL) 1180 { 1181 SafeRelease(&mRenderingParams); 1182 mRenderingParams = renderingParams; 1183 mTextAntialiasMode = textAntialiasMode; 1184 1185 Flush(); 1186 mRT->SetTextRenderingParams(mRenderingParams); 1187 mRT->SetTextAntialiasMode(mTextAntialiasMode); 1188 } 1189 } 1190 1191 DWriteRenderingParams * 1192 DWriteContext::GetRenderingParams( 1193 DWriteRenderingParams *params) 1194 { 1195 if (params != NULL && mRenderingParams != NULL) 1196 { 1197 params->gamma = mRenderingParams->GetGamma(); 1198 params->enhancedContrast = mRenderingParams->GetEnhancedContrast(); 1199 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel(); 1200 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry()); 1201 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode()); 1202 params->textAntialiasMode = mTextAntialiasMode; 1203 } 1204 return params; 1205 } 1206 1207 //////////////////////////////////////////////////////////////////////////// 1208 // PUBLIC C INTERFACES 1209 1210 void 1211 DWrite_Init(void) 1212 { 1213 #ifdef DYNAMIC_DIRECTX 1214 // Load libraries. 1215 hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll")); 1216 hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll")); 1217 if (hD2D1DLL == NULL || hDWriteDLL == NULL) 1218 { 1219 DWrite_Final(); 1220 return; 1221 } 1222 // Get address of procedures. 1223 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress( 1224 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName"); 1225 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL, 1226 "D2D1CreateFactory"); 1227 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL, 1228 "DWriteCreateFactory"); 1229 #endif 1230 } 1231 1232 void 1233 DWrite_Final(void) 1234 { 1235 #ifdef DYNAMIC_DIRECTX 1236 pGetUserDefaultLocaleName = NULL; 1237 pD2D1CreateFactory = NULL; 1238 pDWriteCreateFactory = NULL; 1239 unload(hDWriteDLL); 1240 unload(hD2D1DLL); 1241 #endif 1242 } 1243 1244 DWriteContext * 1245 DWriteContext_Open(void) 1246 { 1247 #ifdef DYNAMIC_DIRECTX 1248 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL 1249 || pDWriteCreateFactory == NULL) 1250 return NULL; 1251 #endif 1252 return new DWriteContext(); 1253 } 1254 1255 void 1256 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect) 1257 { 1258 if (ctx != NULL) 1259 ctx->BindDC(hdc, rect); 1260 } 1261 1262 void 1263 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) 1264 { 1265 if (ctx != NULL) 1266 ctx->SetFont(hFont); 1267 } 1268 1269 void 1270 DWriteContext_DrawText( 1271 DWriteContext *ctx, 1272 const WCHAR *text, 1273 int len, 1274 int x, 1275 int y, 1276 int w, 1277 int h, 1278 int cellWidth, 1279 COLORREF color, 1280 UINT fuOptions, 1281 const RECT *lprc, 1282 const INT *lpDx) 1283 { 1284 if (ctx != NULL) 1285 ctx->DrawText(text, len, x, y, w, h, cellWidth, color, 1286 fuOptions, lprc, lpDx); 1287 } 1288 1289 void 1290 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color) 1291 { 1292 if (ctx != NULL) 1293 ctx->FillRect(rc, color); 1294 } 1295 1296 void 1297 DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2, 1298 COLORREF color) 1299 { 1300 if (ctx != NULL) 1301 ctx->DrawLine(x1, y1, x2, y2, color); 1302 } 1303 1304 void 1305 DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color) 1306 { 1307 if (ctx != NULL) 1308 ctx->SetPixel(x, y, color); 1309 } 1310 1311 void 1312 DWriteContext_Scroll(DWriteContext *ctx, int x, int y, const RECT *rc) 1313 { 1314 if (ctx != NULL) 1315 ctx->Scroll(x, y, rc); 1316 } 1317 1318 void 1319 DWriteContext_Flush(DWriteContext *ctx) 1320 { 1321 if (ctx != NULL) 1322 ctx->Flush(); 1323 } 1324 1325 void 1326 DWriteContext_Close(DWriteContext *ctx) 1327 { 1328 delete ctx; 1329 } 1330 1331 void 1332 DWriteContext_SetRenderingParams( 1333 DWriteContext *ctx, 1334 const DWriteRenderingParams *params) 1335 { 1336 if (ctx != NULL) 1337 ctx->SetRenderingParams(params); 1338 } 1339 1340 DWriteRenderingParams * 1341 DWriteContext_GetRenderingParams( 1342 DWriteContext *ctx, 1343 DWriteRenderingParams *params) 1344 { 1345 if (ctx != NULL) 1346 return ctx->GetRenderingParams(params); 1347 else 1348 return NULL; 1349 } 1350