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