xref: /vim-8.2.3635/src/gui_dwrite.cpp (revision 7f88b65f)
1 /* vi:set ts=8 sts=4 sw=4 noet: */
2 /*
3  * Author: MURAOKA Taro <[email protected]>
4  *
5  * Contributors:
6  *  - Ken Takata
7  *  - Yasuhiro Matsumoto
8  *
9  * Copyright (C) 2013 MURAOKA Taro <[email protected]>
10  * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
11  */
12 
13 #define WIN32_LEAN_AND_MEAN
14 
15 #ifndef DYNAMIC_DIRECTX
16 # if WINVER < 0x0600
17 #  error WINVER must be 0x0600 or above to use DirectWrite(DirectX)
18 # endif
19 #endif
20 
21 #include <windows.h>
22 #include <crtdbg.h>
23 #include <assert.h>
24 #include <math.h>
25 #include <d2d1.h>
26 #include <d2d1helper.h>
27 
28 // Disable these macros to compile with old VC and newer SDK (V8.1 or later).
29 #if defined(_MSC_VER) && (_MSC_VER < 1700)
30 # define _COM_Outptr_ __out
31 # define _In_reads_(s)
32 # define _In_reads_opt_(s)
33 # define _Maybenull_
34 # define _Out_writes_(s)
35 # define _Out_writes_opt_(s)
36 # define _Out_writes_to_(x, y)
37 # define _Out_writes_to_opt_(x, y)
38 # define _Outptr_
39 #endif
40 
41 #ifdef FEAT_DIRECTX_COLOR_EMOJI
42 # include <dwrite_2.h>
43 #else
44 # include <dwrite.h>
45 #endif
46 
47 #include "gui_dwrite.h"
48 
49 #ifdef __MINGW32__
50 # define __maybenull	SAL__maybenull
51 # define __in		SAL__in
52 # define __out		SAL__out
53 #endif
54 
55 #if (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L)
56 # define FINAL final
57 #else
58 # define FINAL
59 #endif
60 
61 #ifdef DYNAMIC_DIRECTX
62 extern "C" HINSTANCE vimLoadLib(char *name);
63 
64 typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int);
65 typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE,
66 	REFIID, const D2D1_FACTORY_OPTIONS *, void **);
67 typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE,
68 	REFIID, IUnknown **);
69 
70 static HINSTANCE hD2D1DLL = NULL;
71 static HINSTANCE hDWriteDLL = NULL;
72 
73 static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL;
74 static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL;
75 static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL;
76 
77 #define GetUserDefaultLocaleName	(*pGetUserDefaultLocaleName)
78 #define D2D1CreateFactory		(*pD2D1CreateFactory)
79 #define DWriteCreateFactory		(*pDWriteCreateFactory)
80 
81     static void
82 unload(HINSTANCE &hinst)
83 {
84     if (hinst != NULL)
85     {
86 	FreeLibrary(hinst);
87 	hinst = NULL;
88     }
89 }
90 #endif // DYNAMIC_DIRECTX
91 
92 template <class T> inline void SafeRelease(T **ppT)
93 {
94     if (*ppT)
95     {
96 	(*ppT)->Release();
97 	*ppT = NULL;
98     }
99 }
100 
101     static DWRITE_PIXEL_GEOMETRY
102 ToPixelGeometry(int value)
103 {
104     switch (value)
105     {
106 	default:
107 	case 0:
108 	    return DWRITE_PIXEL_GEOMETRY_FLAT;
109 	case 1:
110 	    return DWRITE_PIXEL_GEOMETRY_RGB;
111 	case 2:
112 	    return DWRITE_PIXEL_GEOMETRY_BGR;
113     }
114 }
115 
116     static int
117 ToInt(DWRITE_PIXEL_GEOMETRY value)
118 {
119     switch (value)
120     {
121 	case DWRITE_PIXEL_GEOMETRY_FLAT:
122 	    return 0;
123 	case DWRITE_PIXEL_GEOMETRY_RGB:
124 	    return 1;
125 	case DWRITE_PIXEL_GEOMETRY_BGR:
126 	    return 2;
127 	default:
128 	    return -1;
129     }
130 }
131 
132     static DWRITE_RENDERING_MODE
133 ToRenderingMode(int value)
134 {
135     switch (value)
136     {
137 	default:
138 	case 0:
139 	    return DWRITE_RENDERING_MODE_DEFAULT;
140 	case 1:
141 	    return DWRITE_RENDERING_MODE_ALIASED;
142 	case 2:
143 	    return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
144 	case 3:
145 	    return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL;
146 	case 4:
147 	    return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
148 	case 5:
149 	    return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
150 	case 6:
151 	    return DWRITE_RENDERING_MODE_OUTLINE;
152     }
153 }
154 
155     static D2D1_TEXT_ANTIALIAS_MODE
156 ToTextAntialiasMode(int value)
157 {
158     switch (value)
159     {
160 	default:
161 	case 0:
162 	    return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
163 	case 1:
164 	    return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
165 	case 2:
166 	    return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
167 	case 3:
168 	    return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
169     }
170 }
171 
172     static int
173 ToInt(DWRITE_RENDERING_MODE value)
174 {
175     switch (value)
176     {
177 	case DWRITE_RENDERING_MODE_DEFAULT:
178 	    return 0;
179 	case DWRITE_RENDERING_MODE_ALIASED:
180 	    return 1;
181 	case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
182 	    return 2;
183 	case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
184 	    return 3;
185 	case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
186 	    return 4;
187 	case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
188 	    return 5;
189 	case DWRITE_RENDERING_MODE_OUTLINE:
190 	    return 6;
191 	default:
192 	    return -1;
193     }
194 }
195 
196 class FontCache {
197 public:
198     struct Item {
199 	HFONT              hFont;
200 	IDWriteTextFormat* pTextFormat;
201 	DWRITE_FONT_WEIGHT fontWeight;
202 	DWRITE_FONT_STYLE  fontStyle;
203 	Item() : hFont(NULL), pTextFormat(NULL) {}
204     };
205 
206 private:
207     int mSize;
208     Item *mItems;
209 
210 public:
211     FontCache(int size = 2) :
212 	mSize(size),
213 	mItems(new Item[size])
214     {
215     }
216 
217     ~FontCache()
218     {
219 	for (int i = 0; i < mSize; ++i)
220 	    SafeRelease(&mItems[i].pTextFormat);
221 	delete[] mItems;
222     }
223 
224     bool get(HFONT hFont, Item &item)
225     {
226 	int n = find(hFont);
227 	if (n < 0)
228 	    return false;
229 	item = mItems[n];
230 	slide(n);
231 	return true;
232     }
233 
234     void put(const Item& item)
235     {
236 	int n = find(item.hFont);
237 	if (n < 0)
238 	    n = mSize - 1;
239 	if (mItems[n].pTextFormat != item.pTextFormat)
240 	{
241 	    SafeRelease(&mItems[n].pTextFormat);
242 	    item.pTextFormat->AddRef();
243 	}
244 	mItems[n] = item;
245 	slide(n);
246     }
247 
248 private:
249     int find(HFONT hFont)
250     {
251 	for (int i = 0; i < mSize; ++i)
252 	{
253 	    if (mItems[i].hFont == hFont)
254 		return i;
255 	}
256 	return -1;
257     }
258 
259     void slide(int nextTop)
260     {
261 	if (nextTop == 0)
262 	    return;
263 	Item tmp = mItems[nextTop];
264 	for (int i = nextTop - 1; i >= 0; --i)
265 	    mItems[i + 1] = mItems[i];
266 	mItems[0] = tmp;
267     }
268 };
269 
270 enum DrawingMode {
271     DM_GDI = 0,
272     DM_DIRECTX = 1,
273     DM_INTEROP = 2,
274 };
275 
276 struct DWriteContext {
277     HDC mHDC;
278     RECT mBindRect;
279     DrawingMode mDMode;
280     HDC mInteropHDC;
281     bool mDrawing;
282     bool mFallbackDC;
283 
284     ID2D1Factory *mD2D1Factory;
285 
286     ID2D1DCRenderTarget *mRT;
287     ID2D1GdiInteropRenderTarget *mGDIRT;
288     ID2D1SolidColorBrush *mBrush;
289 
290     IDWriteFactory *mDWriteFactory;
291 #ifdef FEAT_DIRECTX_COLOR_EMOJI
292     IDWriteFactory2 *mDWriteFactory2;
293 #endif
294 
295     IDWriteGdiInterop *mGdiInterop;
296     IDWriteRenderingParams *mRenderingParams;
297 
298     FontCache mFontCache;
299     IDWriteTextFormat *mTextFormat;
300     DWRITE_FONT_WEIGHT mFontWeight;
301     DWRITE_FONT_STYLE mFontStyle;
302 
303     D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
304 
305     // METHODS
306 
307     DWriteContext();
308 
309     virtual ~DWriteContext();
310 
311     HRESULT CreateDeviceResources();
312 
313     void DiscardDeviceResources();
314 
315     HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
316 	    IDWriteTextFormat **ppTextFormat);
317 
318     HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
319 
320     void SetFont(HFONT hFont);
321 
322     void BindDC(HDC hdc, const RECT *rect);
323 
324     HRESULT SetDrawingMode(DrawingMode mode);
325 
326     ID2D1Brush* SolidBrush(COLORREF color);
327 
328     void DrawText(const WCHAR *text, int len,
329 	int x, int y, int w, int h, int cellWidth, COLORREF color,
330 	UINT fuOptions, const RECT *lprc, const INT *lpDx);
331 
332     void FillRect(const RECT *rc, COLORREF color);
333 
334     void DrawLine(int x1, int y1, int x2, int y2, COLORREF color);
335 
336     void SetPixel(int x, int y, COLORREF color);
337 
338     void Flush();
339 
340     void SetRenderingParams(
341 	    const DWriteRenderingParams *params);
342 
343     DWriteRenderingParams *GetRenderingParams(
344 	    DWriteRenderingParams *params);
345 };
346 
347 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
348 {
349 private:
350     FLOAT &mAccum;
351     FLOAT mDelta;
352     FLOAT *mAdjustedAdvances;
353 
354 public:
355     AdjustedGlyphRun(
356 	    const DWRITE_GLYPH_RUN *glyphRun,
357 	    FLOAT cellWidth,
358 	    FLOAT &accum) :
359 	DWRITE_GLYPH_RUN(*glyphRun),
360 	mAccum(accum),
361 	mDelta(0.0f),
362 	mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
363     {
364 	assert(cellWidth != 0.0f);
365 	for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
366 	{
367 	    FLOAT orig = glyphRun->glyphAdvances[i];
368 	    FLOAT adjusted = adjustToCell(orig, cellWidth);
369 	    mAdjustedAdvances[i] = adjusted;
370 	    mDelta += adjusted - orig;
371 	}
372 	glyphAdvances = mAdjustedAdvances;
373     }
374 
375     ~AdjustedGlyphRun()
376     {
377 	mAccum += mDelta;
378 	delete[] mAdjustedAdvances;
379     }
380 
381     static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
382     {
383 	int cellCount = int(floor(value / cellWidth + 0.5f));
384 	if (cellCount < 1)
385 	    cellCount = 1;
386 	return cellCount * cellWidth;
387     }
388 };
389 
390 struct TextRendererContext {
391     // const fields.
392     COLORREF color;
393     FLOAT cellWidth;
394 
395     // working fields.
396     FLOAT offsetX;
397 };
398 
399 class TextRenderer FINAL : public IDWriteTextRenderer
400 {
401 public:
402     TextRenderer(
403 	    DWriteContext* pDWC) :
404 	cRefCount_(0),
405 	pDWC_(pDWC)
406     {
407 	AddRef();
408     }
409 
410     // add "virtual" to avoid a compiler warning
411     virtual ~TextRenderer()
412     {
413     }
414 
415     IFACEMETHOD(IsPixelSnappingDisabled)(
416 	__maybenull void* clientDrawingContext,
417 	__out BOOL* isDisabled)
418     {
419 	*isDisabled = FALSE;
420 	return S_OK;
421     }
422 
423     IFACEMETHOD(GetCurrentTransform)(
424 	__maybenull void* clientDrawingContext,
425 	__out DWRITE_MATRIX* transform)
426     {
427 	// forward the render target's transform
428 	pDWC_->mRT->GetTransform(
429 		reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
430 	return S_OK;
431     }
432 
433     IFACEMETHOD(GetPixelsPerDip)(
434 	__maybenull void* clientDrawingContext,
435 	__out FLOAT* pixelsPerDip)
436     {
437 	float dpiX, unused;
438 	pDWC_->mRT->GetDpi(&dpiX, &unused);
439 	*pixelsPerDip = dpiX / 96.0f;
440 	return S_OK;
441     }
442 
443     IFACEMETHOD(DrawUnderline)(
444 	__maybenull void* clientDrawingContext,
445 	FLOAT baselineOriginX,
446 	FLOAT baselineOriginY,
447 	__in DWRITE_UNDERLINE const* underline,
448 	IUnknown* clientDrawingEffect)
449     {
450 	return E_NOTIMPL;
451     }
452 
453     IFACEMETHOD(DrawStrikethrough)(
454 	__maybenull void* clientDrawingContext,
455 	FLOAT baselineOriginX,
456 	FLOAT baselineOriginY,
457 	__in DWRITE_STRIKETHROUGH const* strikethrough,
458 	IUnknown* clientDrawingEffect)
459     {
460 	return E_NOTIMPL;
461     }
462 
463     IFACEMETHOD(DrawInlineObject)(
464 	__maybenull void* clientDrawingContext,
465 	FLOAT originX,
466 	FLOAT originY,
467 	IDWriteInlineObject* inlineObject,
468 	BOOL isSideways,
469 	BOOL isRightToLeft,
470 	IUnknown* clientDrawingEffect)
471     {
472 	return E_NOTIMPL;
473     }
474 
475     IFACEMETHOD(DrawGlyphRun)(
476 	__maybenull void* clientDrawingContext,
477 	FLOAT baselineOriginX,
478 	FLOAT baselineOriginY,
479 	DWRITE_MEASURING_MODE measuringMode,
480 	__in DWRITE_GLYPH_RUN const* glyphRun,
481 	__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
482 	IUnknown* clientDrawingEffect)
483     {
484 	TextRendererContext *context =
485 	    reinterpret_cast<TextRendererContext*>(clientDrawingContext);
486 
487 	AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth,
488 		context->offsetX);
489 
490 #ifdef FEAT_DIRECTX_COLOR_EMOJI
491 	if (pDWC_->mDWriteFactory2 != NULL)
492 	{
493 	    IDWriteColorGlyphRunEnumerator *enumerator = NULL;
494 	    HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun(
495 		baselineOriginX + context->offsetX,
496 		baselineOriginY,
497 		&adjustedGlyphRun,
498 		NULL,
499 		DWRITE_MEASURING_MODE_GDI_NATURAL,
500 		NULL,
501 		0,
502 		&enumerator);
503 	    if (SUCCEEDED(hr))
504 	    {
505 		// Draw by IDWriteFactory2 for color emoji
506 		BOOL hasRun = TRUE;
507 		enumerator->MoveNext(&hasRun);
508 		while (hasRun)
509 		{
510 		    const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun;
511 		    enumerator->GetCurrentRun(&colorGlyphRun);
512 
513 		    pDWC_->mBrush->SetColor(colorGlyphRun->runColor);
514 		    pDWC_->mRT->DrawGlyphRun(
515 			    D2D1::Point2F(
516 				colorGlyphRun->baselineOriginX,
517 				colorGlyphRun->baselineOriginY),
518 			    &colorGlyphRun->glyphRun,
519 			    pDWC_->mBrush,
520 			    DWRITE_MEASURING_MODE_NATURAL);
521 		    enumerator->MoveNext(&hasRun);
522 		}
523 		SafeRelease(&enumerator);
524 		return S_OK;
525 	    }
526 	}
527 #endif
528 
529 	// Draw by IDWriteFactory (without color emoji)
530 	pDWC_->mRT->DrawGlyphRun(
531 		D2D1::Point2F(
532 		    baselineOriginX + context->offsetX,
533 		    baselineOriginY),
534 		&adjustedGlyphRun,
535 		pDWC_->SolidBrush(context->color),
536 		DWRITE_MEASURING_MODE_NATURAL);
537 	return S_OK;
538     }
539 
540 public:
541     IFACEMETHOD_(unsigned long, AddRef) ()
542     {
543 	return InterlockedIncrement(&cRefCount_);
544     }
545 
546     IFACEMETHOD_(unsigned long, Release) ()
547     {
548 	long newCount = InterlockedDecrement(&cRefCount_);
549 
550 	if (newCount == 0)
551 	{
552 	    delete this;
553 	    return 0;
554 	}
555 	return newCount;
556     }
557 
558     IFACEMETHOD(QueryInterface)(
559 	IID const& riid,
560 	void** ppvObject)
561     {
562 	if (__uuidof(IDWriteTextRenderer) == riid)
563 	{
564 	    *ppvObject = this;
565 	}
566 	else if (__uuidof(IDWritePixelSnapping) == riid)
567 	{
568 	    *ppvObject = this;
569 	}
570 	else if (__uuidof(IUnknown) == riid)
571 	{
572 	    *ppvObject = this;
573 	}
574 	else
575 	{
576 	    *ppvObject = NULL;
577 	    return E_FAIL;
578 	}
579 
580 	return S_OK;
581     }
582 
583 private:
584     long cRefCount_;
585     DWriteContext* pDWC_;
586 };
587 
588 DWriteContext::DWriteContext() :
589     mHDC(NULL),
590     mBindRect(),
591     mDMode(DM_GDI),
592     mInteropHDC(NULL),
593     mDrawing(false),
594     mFallbackDC(false),
595     mD2D1Factory(NULL),
596     mRT(NULL),
597     mGDIRT(NULL),
598     mBrush(NULL),
599     mDWriteFactory(NULL),
600 #ifdef FEAT_DIRECTX_COLOR_EMOJI
601     mDWriteFactory2(NULL),
602 #endif
603     mGdiInterop(NULL),
604     mRenderingParams(NULL),
605     mFontCache(8),
606     mTextFormat(NULL),
607     mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
608     mFontStyle(DWRITE_FONT_STYLE_NORMAL),
609     mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
610 {
611     HRESULT hr;
612 
613     hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
614 	    __uuidof(ID2D1Factory), NULL,
615 	    reinterpret_cast<void**>(&mD2D1Factory));
616     _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
617 
618     if (SUCCEEDED(hr))
619 	hr = CreateDeviceResources();
620 
621     if (SUCCEEDED(hr))
622     {
623 	hr = DWriteCreateFactory(
624 		DWRITE_FACTORY_TYPE_SHARED,
625 		__uuidof(IDWriteFactory),
626 		reinterpret_cast<IUnknown**>(&mDWriteFactory));
627 	_RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,
628 		mDWriteFactory);
629     }
630 
631 #ifdef FEAT_DIRECTX_COLOR_EMOJI
632     if (SUCCEEDED(hr))
633     {
634 	DWriteCreateFactory(
635 		DWRITE_FACTORY_TYPE_SHARED,
636 		__uuidof(IDWriteFactory2),
637 		reinterpret_cast<IUnknown**>(&mDWriteFactory2));
638 	_RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available");
639     }
640 #endif
641 
642     if (SUCCEEDED(hr))
643     {
644 	hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
645 	_RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
646     }
647 
648     if (SUCCEEDED(hr))
649     {
650 	hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);
651 	_RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,
652 		mRenderingParams);
653     }
654 }
655 
656 DWriteContext::~DWriteContext()
657 {
658     SafeRelease(&mTextFormat);
659     SafeRelease(&mRenderingParams);
660     SafeRelease(&mGdiInterop);
661     SafeRelease(&mDWriteFactory);
662 #ifdef FEAT_DIRECTX_COLOR_EMOJI
663     SafeRelease(&mDWriteFactory2);
664 #endif
665     SafeRelease(&mBrush);
666     SafeRelease(&mGDIRT);
667     SafeRelease(&mRT);
668     SafeRelease(&mD2D1Factory);
669 }
670 
671     HRESULT
672 DWriteContext::CreateDeviceResources()
673 {
674     HRESULT hr;
675 
676     if (mRT != NULL)
677 	return S_OK;
678 
679     D2D1_RENDER_TARGET_PROPERTIES props = {
680 	D2D1_RENDER_TARGET_TYPE_DEFAULT,
681 	{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },
682 	0, 0,
683 	D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
684 	D2D1_FEATURE_LEVEL_DEFAULT
685     };
686     hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);
687     _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);
688 
689     if (SUCCEEDED(hr))
690     {
691 	// This always succeeds.
692 	mRT->QueryInterface(
693 		__uuidof(ID2D1GdiInteropRenderTarget),
694 		reinterpret_cast<void**>(&mGDIRT));
695 	_RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT);
696     }
697 
698     if (SUCCEEDED(hr))
699     {
700 	hr = mRT->CreateSolidColorBrush(
701 		D2D1::ColorF(D2D1::ColorF::Black),
702 		&mBrush);
703 	_RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);
704     }
705 
706     if (SUCCEEDED(hr))
707     {
708 	if (mHDC != NULL)
709 	{
710 	    mRT->BindDC(mHDC, &mBindRect);
711 	    mRT->SetTransform(D2D1::IdentityMatrix());
712 	}
713     }
714 
715     return hr;
716 }
717 
718     void
719 DWriteContext::DiscardDeviceResources()
720 {
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_CENTER);
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::BindDC(HDC hdc, const RECT *rect)
903 {
904     Flush();
905     mRT->BindDC(hdc, rect);
906     mRT->SetTransform(D2D1::IdentityMatrix());
907     mHDC = hdc;
908     mBindRect = *rect;
909 }
910 
911     HRESULT
912 DWriteContext::SetDrawingMode(DrawingMode mode)
913 {
914     HRESULT hr = S_OK;
915 
916     switch (mode)
917     {
918 	default:
919 	case DM_GDI:
920 	    if (mInteropHDC != NULL)
921 	    {
922 		mGDIRT->ReleaseDC(NULL);
923 		mInteropHDC = NULL;
924 	    }
925 	    if (mDrawing)
926 	    {
927 		hr = mRT->EndDraw();
928 		if (hr == D2DERR_RECREATE_TARGET)
929 		{
930 		    hr = S_OK;
931 		    DiscardDeviceResources();
932 		    CreateDeviceResources();
933 		}
934 		mDrawing = false;
935 	    }
936 	    break;
937 
938 	case DM_DIRECTX:
939 	    if (mInteropHDC != NULL)
940 	    {
941 		mGDIRT->ReleaseDC(NULL);
942 		mInteropHDC = NULL;
943 	    }
944 	    else if (mDrawing == false)
945 	    {
946 		CreateDeviceResources();
947 		mRT->BeginDraw();
948 		mDrawing = true;
949 	    }
950 	    break;
951 
952 	case DM_INTEROP:
953 	    if (mDrawing == false)
954 	    {
955 		CreateDeviceResources();
956 		mRT->BeginDraw();
957 		mDrawing = true;
958 	    }
959 	    if (mInteropHDC == NULL)
960 		hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC);
961 	    break;
962     }
963     mDMode = mode;
964     return hr;
965 }
966 
967     ID2D1Brush*
968 DWriteContext::SolidBrush(COLORREF color)
969 {
970     mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 |
971 		UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color))));
972     return mBrush;
973 }
974 
975     void
976 DWriteContext::DrawText(const WCHAR *text, int len,
977 	int x, int y, int w, int h, int cellWidth, COLORREF color,
978 	UINT fuOptions, const RECT *lprc, const INT *lpDx)
979 {
980     if (mFallbackDC)
981     {
982 	// Fall back to GDI rendering.
983 	HRESULT hr = SetDrawingMode(DM_INTEROP);
984 	if (SUCCEEDED(hr))
985 	{
986 	    HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT);
987 	    HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont);
988 	    ::SetTextColor(mInteropHDC, color);
989 	    ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC));
990 	    ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx);
991 	    ::SelectObject(mInteropHDC, hOldFont);
992 	}
993 	return;
994     }
995 
996     HRESULT hr;
997     IDWriteTextLayout *textLayout = NULL;
998 
999     SetDrawingMode(DM_DIRECTX);
1000 
1001     hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
1002 	    FLOAT(w), FLOAT(h), &textLayout);
1003 
1004     if (SUCCEEDED(hr))
1005     {
1006 	DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
1007 	textLayout->SetFontWeight(mFontWeight, textRange);
1008 	textLayout->SetFontStyle(mFontStyle, textRange);
1009 
1010 	TextRenderer renderer(this);
1011 	TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
1012 	textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y) - 0.5f);
1013     }
1014 
1015     SafeRelease(&textLayout);
1016 }
1017 
1018     void
1019 DWriteContext::FillRect(const RECT *rc, COLORREF color)
1020 {
1021     if (mDMode == DM_INTEROP)
1022     {
1023 	// GDI functions are used before this call.  Keep using GDI.
1024 	// (Switching to Direct2D causes terrible slowdown.)
1025 	HBRUSH hbr = ::CreateSolidBrush(color);
1026 	::FillRect(mInteropHDC, rc, hbr);
1027 	::DeleteObject(HGDIOBJ(hbr));
1028     }
1029     else
1030     {
1031 	SetDrawingMode(DM_DIRECTX);
1032 	mRT->FillRectangle(
1033 		D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top),
1034 		    FLOAT(rc->right), FLOAT(rc->bottom)),
1035 		SolidBrush(color));
1036     }
1037 }
1038 
1039     void
1040 DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color)
1041 {
1042     if (mDMode == DM_INTEROP)
1043     {
1044 	// GDI functions are used before this call.  Keep using GDI.
1045 	// (Switching to Direct2D causes terrible slowdown.)
1046 	HPEN hpen = ::CreatePen(PS_SOLID, 1, color);
1047 	HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen));
1048 	::MoveToEx(mInteropHDC, x1, y1, NULL);
1049 	::LineTo(mInteropHDC, x2, y2);
1050 	::SelectObject(mInteropHDC, old_pen);
1051 	::DeleteObject(HGDIOBJ(hpen));
1052     }
1053     else
1054     {
1055 	SetDrawingMode(DM_DIRECTX);
1056 	mRT->DrawLine(
1057 		D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f),
1058 		D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f),
1059 		SolidBrush(color));
1060     }
1061 }
1062 
1063     void
1064 DWriteContext::SetPixel(int x, int y, COLORREF color)
1065 {
1066     if (mDMode == DM_INTEROP)
1067     {
1068 	// GDI functions are used before this call.  Keep using GDI.
1069 	// (Switching to Direct2D causes terrible slowdown.)
1070 	::SetPixel(mInteropHDC, x, y, color);
1071     }
1072     else
1073     {
1074 	SetDrawingMode(DM_DIRECTX);
1075 	// Direct2D doesn't have SetPixel API.  Use DrawLine instead.
1076 	mRT->DrawLine(
1077 		D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f),
1078 		D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f),
1079 		SolidBrush(color));
1080     }
1081 }
1082 
1083     void
1084 DWriteContext::Flush()
1085 {
1086     SetDrawingMode(DM_GDI);
1087 }
1088 
1089     void
1090 DWriteContext::SetRenderingParams(
1091 	const DWriteRenderingParams *params)
1092 {
1093     if (mDWriteFactory == NULL)
1094 	return;
1095 
1096     IDWriteRenderingParams *renderingParams = NULL;
1097     D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =
1098 	D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
1099     HRESULT hr;
1100     if (params != NULL)
1101     {
1102 	hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,
1103 		params->enhancedContrast, params->clearTypeLevel,
1104 		ToPixelGeometry(params->pixelGeometry),
1105 		ToRenderingMode(params->renderingMode), &renderingParams);
1106 	textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);
1107     }
1108     else
1109 	hr = mDWriteFactory->CreateRenderingParams(&renderingParams);
1110     if (SUCCEEDED(hr) && renderingParams != NULL)
1111     {
1112 	SafeRelease(&mRenderingParams);
1113 	mRenderingParams = renderingParams;
1114 	mTextAntialiasMode = textAntialiasMode;
1115 
1116 	Flush();
1117 	mRT->SetTextRenderingParams(mRenderingParams);
1118 	mRT->SetTextAntialiasMode(mTextAntialiasMode);
1119     }
1120 }
1121 
1122     DWriteRenderingParams *
1123 DWriteContext::GetRenderingParams(
1124 	DWriteRenderingParams *params)
1125 {
1126     if (params != NULL && mRenderingParams != NULL)
1127     {
1128 	params->gamma = mRenderingParams->GetGamma();
1129 	params->enhancedContrast = mRenderingParams->GetEnhancedContrast();
1130 	params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();
1131 	params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());
1132 	params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());
1133 	params->textAntialiasMode = mTextAntialiasMode;
1134     }
1135     return params;
1136 }
1137 
1138 ////////////////////////////////////////////////////////////////////////////
1139 // PUBLIC C INTERFACES
1140 
1141     void
1142 DWrite_Init(void)
1143 {
1144 #ifdef DYNAMIC_DIRECTX
1145     // Load libraries.
1146     hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll"));
1147     hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll"));
1148     if (hD2D1DLL == NULL || hDWriteDLL == NULL)
1149     {
1150 	DWrite_Final();
1151 	return;
1152     }
1153     // Get address of procedures.
1154     pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(
1155 	    GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");
1156     pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,
1157 	    "D2D1CreateFactory");
1158     pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,
1159 	    "DWriteCreateFactory");
1160 #endif
1161 }
1162 
1163     void
1164 DWrite_Final(void)
1165 {
1166 #ifdef DYNAMIC_DIRECTX
1167     pGetUserDefaultLocaleName = NULL;
1168     pD2D1CreateFactory = NULL;
1169     pDWriteCreateFactory = NULL;
1170     unload(hDWriteDLL);
1171     unload(hD2D1DLL);
1172 #endif
1173 }
1174 
1175     DWriteContext *
1176 DWriteContext_Open(void)
1177 {
1178 #ifdef DYNAMIC_DIRECTX
1179     if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL
1180 	    || pDWriteCreateFactory == NULL)
1181 	return NULL;
1182 #endif
1183     return new DWriteContext();
1184 }
1185 
1186     void
1187 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect)
1188 {
1189     if (ctx != NULL)
1190 	ctx->BindDC(hdc, rect);
1191 }
1192 
1193     void
1194 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
1195 {
1196     if (ctx != NULL)
1197 	ctx->SetFont(hFont);
1198 }
1199 
1200     void
1201 DWriteContext_DrawText(
1202 	DWriteContext *ctx,
1203 	const WCHAR *text,
1204 	int len,
1205 	int x,
1206 	int y,
1207 	int w,
1208 	int h,
1209 	int cellWidth,
1210 	COLORREF color,
1211 	UINT fuOptions,
1212 	const RECT *lprc,
1213 	const INT *lpDx)
1214 {
1215     if (ctx != NULL)
1216 	ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
1217 		fuOptions, lprc, lpDx);
1218 }
1219 
1220     void
1221 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color)
1222 {
1223     if (ctx != NULL)
1224 	ctx->FillRect(rc, color);
1225 }
1226 
1227     void
1228 DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2,
1229 	COLORREF color)
1230 {
1231     if (ctx != NULL)
1232 	ctx->DrawLine(x1, y1, x2, y2, color);
1233 }
1234 
1235     void
1236 DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color)
1237 {
1238     if (ctx != NULL)
1239 	ctx->SetPixel(x, y, color);
1240 }
1241 
1242     void
1243 DWriteContext_Flush(DWriteContext *ctx)
1244 {
1245     if (ctx != NULL)
1246 	ctx->Flush();
1247 }
1248 
1249     void
1250 DWriteContext_Close(DWriteContext *ctx)
1251 {
1252     delete ctx;
1253 }
1254 
1255     void
1256 DWriteContext_SetRenderingParams(
1257 	DWriteContext *ctx,
1258 	const DWriteRenderingParams *params)
1259 {
1260     if (ctx != NULL)
1261 	ctx->SetRenderingParams(params);
1262 }
1263 
1264     DWriteRenderingParams *
1265 DWriteContext_GetRenderingParams(
1266 	DWriteContext *ctx,
1267 	DWriteRenderingParams *params)
1268 {
1269     if (ctx != NULL)
1270 	return ctx->GetRenderingParams(params);
1271     else
1272 	return NULL;
1273 }
1274