1 /* vi:set ts=8 sts=4 sw=4 noet: */
2 /*
3 * Author: MURAOKA Taro <koron.kaoriya@gmail.com>
4 *
5 * Contributors:
6 * - Ken Takata
7 * - Yasuhiro Matsumoto
8 *
9 * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
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
unload(HINSTANCE & hinst)82 unload(HINSTANCE &hinst)
83 {
84 if (hinst != NULL)
85 {
86 FreeLibrary(hinst);
87 hinst = NULL;
88 }
89 }
90 #endif // DYNAMIC_DIRECTX
91
SafeRelease(T ** ppT)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
ToPixelGeometry(int value)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
ToInt(DWRITE_PIXEL_GEOMETRY value)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
ToRenderingMode(int value)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
ToTextAntialiasMode(int value)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
ToInt(DWRITE_RENDERING_MODE value)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;
ItemFontCache::Item203 Item() : hFont(NULL), pTextFormat(NULL) {}
204 };
205
206 private:
207 int mSize;
208 Item *mItems;
209
210 public:
FontCache(int size=2)211 FontCache(int size = 2) :
212 mSize(size),
213 mItems(new Item[size])
214 {
215 }
216
~FontCache()217 ~FontCache()
218 {
219 for (int i = 0; i < mSize; ++i)
220 SafeRelease(&mItems[i].pTextFormat);
221 delete[] mItems;
222 }
223
get(HFONT hFont,Item & item)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
put(const Item & item)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:
find(HFONT hFont)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
slide(int nextTop)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:
AdjustedGlyphRun(const DWRITE_GLYPH_RUN * glyphRun,FLOAT cellWidth,FLOAT & accum)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
~AdjustedGlyphRun()381 ~AdjustedGlyphRun()
382 {
383 mAccum += mDelta;
384 delete[] mAdjustedAdvances;
385 }
386
adjustToCell(FLOAT value,FLOAT cellWidth)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:
TextRenderer(DWriteContext * pDWC)408 TextRenderer(
409 DWriteContext* pDWC) :
410 cRefCount_(0),
411 pDWC_(pDWC)
412 {
413 AddRef();
414 }
415
416 // add "virtual" to avoid a compiler warning
~TextRenderer()417 virtual ~TextRenderer()
418 {
419 }
420
IFACEMETHOD(IsPixelSnappingDisabled)421 IFACEMETHOD(IsPixelSnappingDisabled)(
422 __maybenull void* clientDrawingContext,
423 __out BOOL* isDisabled)
424 {
425 *isDisabled = FALSE;
426 return S_OK;
427 }
428
IFACEMETHOD(GetCurrentTransform)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
IFACEMETHOD(GetPixelsPerDip)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
IFACEMETHOD(DrawUnderline)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
IFACEMETHOD(DrawStrikethrough)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
IFACEMETHOD(DrawInlineObject)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
IFACEMETHOD(DrawGlyphRun)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:
IFACEMETHOD_(unsigned long,AddRef)547 IFACEMETHOD_(unsigned long, AddRef) ()
548 {
549 return InterlockedIncrement(&cRefCount_);
550 }
551
IFACEMETHOD_(unsigned long,Release)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
IFACEMETHOD(QueryInterface)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
DWriteContext()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
~DWriteContext()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
CreateDeviceResources()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
DiscardDeviceResources()718 DWriteContext::DiscardDeviceResources()
719 {
720 SafeRelease(&mBitmap);
721 SafeRelease(&mBrush);
722 SafeRelease(&mGDIRT);
723 SafeRelease(&mRT);
724 }
725
726 HRESULT
CreateTextFormatFromLOGFONT(const LOGFONTW & logFont,IDWriteTextFormat ** ppTextFormat)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
SetFontByLOGFONT(const LOGFONTW & logFont)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
SetFont(HFONT hFont)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
Rebind()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
BindDC(HDC hdc,const RECT * rect)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
SetDrawingMode(DrawingMode mode)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*
SolidBrush(COLORREF color)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
DrawText(const WCHAR * text,int len,int x,int y,int w,int h,int cellWidth,COLORREF color,UINT fuOptions,const RECT * lprc,const INT * lpDx)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
FillRect(const RECT * rc,COLORREF color)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
DrawLine(int x1,int y1,int x2,int y2,COLORREF color)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
SetPixel(int x,int y,COLORREF color)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
Scroll(int x,int y,const RECT * rc)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
Flush()1153 DWriteContext::Flush()
1154 {
1155 SetDrawingMode(DM_GDI);
1156 }
1157
1158 void
SetRenderingParams(const DWriteRenderingParams * params)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 *
GetRenderingParams(DWriteRenderingParams * params)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
DWrite_Init(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
DWrite_Final(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 *
DWriteContext_Open(void)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
DWriteContext_BindDC(DWriteContext * ctx,HDC hdc,const RECT * rect)1256 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect)
1257 {
1258 if (ctx != NULL)
1259 ctx->BindDC(hdc, rect);
1260 }
1261
1262 void
DWriteContext_SetFont(DWriteContext * ctx,HFONT hFont)1263 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
1264 {
1265 if (ctx != NULL)
1266 ctx->SetFont(hFont);
1267 }
1268
1269 void
DWriteContext_DrawText(DWriteContext * ctx,const WCHAR * text,int len,int x,int y,int w,int h,int cellWidth,COLORREF color,UINT fuOptions,const RECT * lprc,const INT * lpDx)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
DWriteContext_FillRect(DWriteContext * ctx,const RECT * rc,COLORREF color)1290 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color)
1291 {
1292 if (ctx != NULL)
1293 ctx->FillRect(rc, color);
1294 }
1295
1296 void
DWriteContext_DrawLine(DWriteContext * ctx,int x1,int y1,int x2,int y2,COLORREF color)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
DWriteContext_SetPixel(DWriteContext * ctx,int x,int y,COLORREF color)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
DWriteContext_Scroll(DWriteContext * ctx,int x,int y,const RECT * rc)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
DWriteContext_Flush(DWriteContext * ctx)1319 DWriteContext_Flush(DWriteContext *ctx)
1320 {
1321 if (ctx != NULL)
1322 ctx->Flush();
1323 }
1324
1325 void
DWriteContext_Close(DWriteContext * ctx)1326 DWriteContext_Close(DWriteContext *ctx)
1327 {
1328 delete ctx;
1329 }
1330
1331 void
DWriteContext_SetRenderingParams(DWriteContext * ctx,const DWriteRenderingParams * params)1332 DWriteContext_SetRenderingParams(
1333 DWriteContext *ctx,
1334 const DWriteRenderingParams *params)
1335 {
1336 if (ctx != NULL)
1337 ctx->SetRenderingParams(params);
1338 }
1339
1340 DWriteRenderingParams *
DWriteContext_GetRenderingParams(DWriteContext * ctx,DWriteRenderingParams * params)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