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