xref: /vim-8.2.3635/src/gui_dwrite.cpp (revision 788fbb47)
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     ID2D1Bitmap *mBitmap;
290 
291     IDWriteFactory *mDWriteFactory;
292 #ifdef FEAT_DIRECTX_COLOR_EMOJI
293     IDWriteFactory2 *mDWriteFactory2;
294 #endif
295 
296     IDWriteGdiInterop *mGdiInterop;
297     IDWriteRenderingParams *mRenderingParams;
298 
299     FontCache mFontCache;
300     IDWriteTextFormat *mTextFormat;
301     DWRITE_FONT_WEIGHT mFontWeight;
302     DWRITE_FONT_STYLE mFontStyle;
303 
304     D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
305 
306     // METHODS
307 
308     DWriteContext();
309 
310     virtual ~DWriteContext();
311 
312     HRESULT CreateDeviceResources();
313 
314     void DiscardDeviceResources();
315 
316     HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
317 	    IDWriteTextFormat **ppTextFormat);
318 
319     HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
320 
321     void SetFont(HFONT hFont);
322 
323     void Rebind();
324 
325     void BindDC(HDC hdc, const RECT *rect);
326 
327     HRESULT SetDrawingMode(DrawingMode mode);
328 
329     ID2D1Brush* SolidBrush(COLORREF color);
330 
331     void DrawText(const WCHAR *text, int len,
332 	int x, int y, int w, int h, int cellWidth, COLORREF color,
333 	UINT fuOptions, const RECT *lprc, const INT *lpDx);
334 
335     void FillRect(const RECT *rc, COLORREF color);
336 
337     void DrawLine(int x1, int y1, int x2, int y2, COLORREF color);
338 
339     void SetPixel(int x, int y, COLORREF color);
340 
341     void Scroll(int x, int y, const RECT *rc);
342 
343     void Flush();
344 
345     void SetRenderingParams(
346 	    const DWriteRenderingParams *params);
347 
348     DWriteRenderingParams *GetRenderingParams(
349 	    DWriteRenderingParams *params);
350 };
351 
352 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
353 {
354 private:
355     FLOAT &mAccum;
356     FLOAT mDelta;
357     FLOAT *mAdjustedAdvances;
358 
359 public:
360     AdjustedGlyphRun(
361 	    const DWRITE_GLYPH_RUN *glyphRun,
362 	    FLOAT cellWidth,
363 	    FLOAT &accum) :
364 	DWRITE_GLYPH_RUN(*glyphRun),
365 	mAccum(accum),
366 	mDelta(0.0f),
367 	mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
368     {
369 	assert(cellWidth != 0.0f);
370 	for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
371 	{
372 	    FLOAT orig = glyphRun->glyphAdvances[i];
373 	    FLOAT adjusted = adjustToCell(orig, cellWidth);
374 	    mAdjustedAdvances[i] = adjusted;
375 	    mDelta += adjusted - orig;
376 	}
377 	glyphAdvances = mAdjustedAdvances;
378     }
379 
380     ~AdjustedGlyphRun()
381     {
382 	mAccum += mDelta;
383 	delete[] mAdjustedAdvances;
384     }
385 
386     static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
387     {
388 	int cellCount = int(floor(value / cellWidth + 0.5f));
389 	if (cellCount < 1)
390 	    cellCount = 1;
391 	return cellCount * cellWidth;
392     }
393 };
394 
395 struct TextRendererContext {
396     // const fields.
397     COLORREF color;
398     FLOAT cellWidth;
399 
400     // working fields.
401     FLOAT offsetX;
402 };
403 
404 class TextRenderer FINAL : public IDWriteTextRenderer
405 {
406 public:
407     TextRenderer(
408 	    DWriteContext* pDWC) :
409 	cRefCount_(0),
410 	pDWC_(pDWC)
411     {
412 	AddRef();
413     }
414 
415     // add "virtual" to avoid a compiler warning
416     virtual ~TextRenderer()
417     {
418     }
419 
420     IFACEMETHOD(IsPixelSnappingDisabled)(
421 	__maybenull void* clientDrawingContext,
422 	__out BOOL* isDisabled)
423     {
424 	*isDisabled = FALSE;
425 	return S_OK;
426     }
427 
428     IFACEMETHOD(GetCurrentTransform)(
429 	__maybenull void* clientDrawingContext,
430 	__out DWRITE_MATRIX* transform)
431     {
432 	// forward the render target's transform
433 	pDWC_->mRT->GetTransform(
434 		reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
435 	return S_OK;
436     }
437 
438     IFACEMETHOD(GetPixelsPerDip)(
439 	__maybenull void* clientDrawingContext,
440 	__out FLOAT* pixelsPerDip)
441     {
442 	float dpiX, unused;
443 	pDWC_->mRT->GetDpi(&dpiX, &unused);
444 	*pixelsPerDip = dpiX / 96.0f;
445 	return S_OK;
446     }
447 
448     IFACEMETHOD(DrawUnderline)(
449 	__maybenull void* clientDrawingContext,
450 	FLOAT baselineOriginX,
451 	FLOAT baselineOriginY,
452 	__in DWRITE_UNDERLINE const* underline,
453 	IUnknown* clientDrawingEffect)
454     {
455 	return E_NOTIMPL;
456     }
457 
458     IFACEMETHOD(DrawStrikethrough)(
459 	__maybenull void* clientDrawingContext,
460 	FLOAT baselineOriginX,
461 	FLOAT baselineOriginY,
462 	__in DWRITE_STRIKETHROUGH const* strikethrough,
463 	IUnknown* clientDrawingEffect)
464     {
465 	return E_NOTIMPL;
466     }
467 
468     IFACEMETHOD(DrawInlineObject)(
469 	__maybenull void* clientDrawingContext,
470 	FLOAT originX,
471 	FLOAT originY,
472 	IDWriteInlineObject* inlineObject,
473 	BOOL isSideways,
474 	BOOL isRightToLeft,
475 	IUnknown* clientDrawingEffect)
476     {
477 	return E_NOTIMPL;
478     }
479 
480     IFACEMETHOD(DrawGlyphRun)(
481 	__maybenull void* clientDrawingContext,
482 	FLOAT baselineOriginX,
483 	FLOAT baselineOriginY,
484 	DWRITE_MEASURING_MODE measuringMode,
485 	__in DWRITE_GLYPH_RUN const* glyphRun,
486 	__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
487 	IUnknown* clientDrawingEffect)
488     {
489 	TextRendererContext *context =
490 	    reinterpret_cast<TextRendererContext*>(clientDrawingContext);
491 
492 	AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth,
493 		context->offsetX);
494 
495 #ifdef FEAT_DIRECTX_COLOR_EMOJI
496 	if (pDWC_->mDWriteFactory2 != NULL)
497 	{
498 	    IDWriteColorGlyphRunEnumerator *enumerator = NULL;
499 	    HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun(
500 		baselineOriginX + context->offsetX,
501 		baselineOriginY,
502 		&adjustedGlyphRun,
503 		NULL,
504 		DWRITE_MEASURING_MODE_GDI_NATURAL,
505 		NULL,
506 		0,
507 		&enumerator);
508 	    if (SUCCEEDED(hr))
509 	    {
510 		// Draw by IDWriteFactory2 for color emoji
511 		BOOL hasRun = TRUE;
512 		enumerator->MoveNext(&hasRun);
513 		while (hasRun)
514 		{
515 		    const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun;
516 		    enumerator->GetCurrentRun(&colorGlyphRun);
517 
518 		    pDWC_->mBrush->SetColor(colorGlyphRun->runColor);
519 		    pDWC_->mRT->DrawGlyphRun(
520 			    D2D1::Point2F(
521 				colorGlyphRun->baselineOriginX,
522 				colorGlyphRun->baselineOriginY),
523 			    &colorGlyphRun->glyphRun,
524 			    pDWC_->mBrush,
525 			    DWRITE_MEASURING_MODE_NATURAL);
526 		    enumerator->MoveNext(&hasRun);
527 		}
528 		SafeRelease(&enumerator);
529 		return S_OK;
530 	    }
531 	}
532 #endif
533 
534 	// Draw by IDWriteFactory (without color emoji)
535 	pDWC_->mRT->DrawGlyphRun(
536 		D2D1::Point2F(
537 		    baselineOriginX + context->offsetX,
538 		    baselineOriginY),
539 		&adjustedGlyphRun,
540 		pDWC_->SolidBrush(context->color),
541 		DWRITE_MEASURING_MODE_NATURAL);
542 	return S_OK;
543     }
544 
545 public:
546     IFACEMETHOD_(unsigned long, AddRef) ()
547     {
548 	return InterlockedIncrement(&cRefCount_);
549     }
550 
551     IFACEMETHOD_(unsigned long, Release) ()
552     {
553 	long newCount = InterlockedDecrement(&cRefCount_);
554 
555 	if (newCount == 0)
556 	{
557 	    delete this;
558 	    return 0;
559 	}
560 	return newCount;
561     }
562 
563     IFACEMETHOD(QueryInterface)(
564 	IID const& riid,
565 	void** ppvObject)
566     {
567 	if (__uuidof(IDWriteTextRenderer) == riid)
568 	{
569 	    *ppvObject = this;
570 	}
571 	else if (__uuidof(IDWritePixelSnapping) == riid)
572 	{
573 	    *ppvObject = this;
574 	}
575 	else if (__uuidof(IUnknown) == riid)
576 	{
577 	    *ppvObject = this;
578 	}
579 	else
580 	{
581 	    *ppvObject = NULL;
582 	    return E_FAIL;
583 	}
584 
585 	return S_OK;
586     }
587 
588 private:
589     long cRefCount_;
590     DWriteContext* pDWC_;
591 };
592 
593 DWriteContext::DWriteContext() :
594     mHDC(NULL),
595     mBindRect(),
596     mDMode(DM_GDI),
597     mInteropHDC(NULL),
598     mDrawing(false),
599     mFallbackDC(false),
600     mD2D1Factory(NULL),
601     mRT(NULL),
602     mGDIRT(NULL),
603     mBrush(NULL),
604     mBitmap(NULL),
605     mDWriteFactory(NULL),
606 #ifdef FEAT_DIRECTX_COLOR_EMOJI
607     mDWriteFactory2(NULL),
608 #endif
609     mGdiInterop(NULL),
610     mRenderingParams(NULL),
611     mFontCache(8),
612     mTextFormat(NULL),
613     mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
614     mFontStyle(DWRITE_FONT_STYLE_NORMAL),
615     mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
616 {
617     HRESULT hr;
618 
619     hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
620 	    __uuidof(ID2D1Factory), NULL,
621 	    reinterpret_cast<void**>(&mD2D1Factory));
622     _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
623 
624     if (SUCCEEDED(hr))
625     {
626 	hr = DWriteCreateFactory(
627 		DWRITE_FACTORY_TYPE_SHARED,
628 		__uuidof(IDWriteFactory),
629 		reinterpret_cast<IUnknown**>(&mDWriteFactory));
630 	_RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,
631 		mDWriteFactory);
632     }
633 
634 #ifdef FEAT_DIRECTX_COLOR_EMOJI
635     if (SUCCEEDED(hr))
636     {
637 	DWriteCreateFactory(
638 		DWRITE_FACTORY_TYPE_SHARED,
639 		__uuidof(IDWriteFactory2),
640 		reinterpret_cast<IUnknown**>(&mDWriteFactory2));
641 	_RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available");
642     }
643 #endif
644 
645     if (SUCCEEDED(hr))
646     {
647 	hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
648 	_RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
649     }
650 
651     if (SUCCEEDED(hr))
652     {
653 	hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);
654 	_RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,
655 		mRenderingParams);
656     }
657 }
658 
659 DWriteContext::~DWriteContext()
660 {
661     SafeRelease(&mTextFormat);
662     SafeRelease(&mRenderingParams);
663     SafeRelease(&mGdiInterop);
664     SafeRelease(&mDWriteFactory);
665 #ifdef FEAT_DIRECTX_COLOR_EMOJI
666     SafeRelease(&mDWriteFactory2);
667 #endif
668     SafeRelease(&mBitmap);
669     SafeRelease(&mBrush);
670     SafeRelease(&mGDIRT);
671     SafeRelease(&mRT);
672     SafeRelease(&mD2D1Factory);
673 }
674 
675     HRESULT
676 DWriteContext::CreateDeviceResources()
677 {
678     HRESULT hr;
679 
680     if (mRT != NULL)
681 	return S_OK;
682 
683     D2D1_RENDER_TARGET_PROPERTIES props = {
684 	D2D1_RENDER_TARGET_TYPE_DEFAULT,
685 	{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },
686 	0, 0,
687 	D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
688 	D2D1_FEATURE_LEVEL_DEFAULT
689     };
690     hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);
691     _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);
692 
693     if (SUCCEEDED(hr))
694     {
695 	// This always succeeds.
696 	mRT->QueryInterface(
697 		__uuidof(ID2D1GdiInteropRenderTarget),
698 		reinterpret_cast<void**>(&mGDIRT));
699 	_RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT);
700     }
701 
702     if (SUCCEEDED(hr))
703     {
704 	hr = mRT->CreateSolidColorBrush(
705 		D2D1::ColorF(D2D1::ColorF::Black),
706 		&mBrush);
707 	_RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);
708     }
709 
710     if (SUCCEEDED(hr))
711 	Rebind();
712 
713     return hr;
714 }
715 
716     void
717 DWriteContext::DiscardDeviceResources()
718 {
719     SafeRelease(&mBitmap);
720     SafeRelease(&mBrush);
721     SafeRelease(&mGDIRT);
722     SafeRelease(&mRT);
723 }
724 
725     HRESULT
726 DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
727 	IDWriteTextFormat **ppTextFormat)
728 {
729     // Most of this function is copied from: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/RenderTest/TextHelpers.cpp
730     HRESULT hr = S_OK;
731     IDWriteTextFormat *pTextFormat = NULL;
732 
733     IDWriteFont *font = NULL;
734     IDWriteFontFamily *fontFamily = NULL;
735     IDWriteLocalizedStrings *localizedFamilyNames = NULL;
736     float fontSize = 0;
737 
738     if (SUCCEEDED(hr))
739     {
740 	hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
741     }
742 
743     // Get the font family to which this font belongs.
744     if (SUCCEEDED(hr))
745     {
746 	hr = font->GetFontFamily(&fontFamily);
747     }
748 
749     // Get the family names. This returns an object that encapsulates one or
750     // more names with the same meaning but in different languages.
751     if (SUCCEEDED(hr))
752     {
753 	hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
754     }
755 
756     // Get the family name at index zero. If we were going to display the name
757     // we'd want to try to find one that matched the use locale, but for
758     // purposes of creating a text format object any language will do.
759 
760     wchar_t familyName[100];
761     if (SUCCEEDED(hr))
762     {
763 	hr = localizedFamilyNames->GetString(0, familyName,
764 		ARRAYSIZE(familyName));
765     }
766 
767     if (SUCCEEDED(hr))
768     {
769 	// Use lfHeight of the LOGFONT as font size.
770 	fontSize = float(logFont.lfHeight);
771 
772 	if (fontSize < 0)
773 	{
774 	    // Negative lfHeight represents the size of the em unit.
775 	    fontSize = -fontSize;
776 	}
777 	else
778 	{
779 	    // Positive lfHeight represents the cell height (ascent +
780 	    // descent).
781 	    DWRITE_FONT_METRICS fontMetrics;
782 	    font->GetMetrics(&fontMetrics);
783 
784 	    // Convert the cell height (ascent + descent) from design units
785 	    // to ems.
786 	    float cellHeight = static_cast<float>(
787 		    fontMetrics.ascent + fontMetrics.descent)
788 		/ fontMetrics.designUnitsPerEm;
789 
790 	    // Divide the font size by the cell height to get the font em
791 	    // size.
792 	    fontSize /= cellHeight;
793 	}
794     }
795 
796     // The text format includes a locale name. Ideally, this would be the
797     // language of the text, which may or may not be the same as the primary
798     // language of the user. However, for our purposes the user locale will do.
799     wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
800     if (SUCCEEDED(hr))
801     {
802 	if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
803 	    hr = HRESULT_FROM_WIN32(GetLastError());
804     }
805 
806     if (SUCCEEDED(hr))
807     {
808 	// Create the text format object.
809 	hr = mDWriteFactory->CreateTextFormat(
810 		familyName,
811 		NULL, // no custom font collection
812 		font->GetWeight(),
813 		font->GetStyle(),
814 		font->GetStretch(),
815 		fontSize,
816 		localeName,
817 		&pTextFormat);
818     }
819 
820     if (SUCCEEDED(hr))
821 	hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
822 
823     if (SUCCEEDED(hr))
824 	hr = pTextFormat->SetParagraphAlignment(
825 		DWRITE_PARAGRAPH_ALIGNMENT_FAR);
826 
827     if (SUCCEEDED(hr))
828 	hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
829 
830     SafeRelease(&localizedFamilyNames);
831     SafeRelease(&fontFamily);
832     SafeRelease(&font);
833 
834     if (SUCCEEDED(hr))
835 	*ppTextFormat = pTextFormat;
836     else
837 	SafeRelease(&pTextFormat);
838 
839     return hr;
840 }
841 
842     HRESULT
843 DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont)
844 {
845     HRESULT hr = S_OK;
846     IDWriteTextFormat *pTextFormat = NULL;
847 
848     hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat);
849 
850     if (SUCCEEDED(hr))
851     {
852 	SafeRelease(&mTextFormat);
853 	mTextFormat = pTextFormat;
854 	mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
855 	mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
856 	    : DWRITE_FONT_STYLE_NORMAL;
857     }
858 
859     return hr;
860 }
861 
862     void
863 DWriteContext::SetFont(HFONT hFont)
864 {
865     FontCache::Item item;
866     if (mFontCache.get(hFont, item))
867     {
868 	if (item.pTextFormat != NULL)
869 	{
870 	    item.pTextFormat->AddRef();
871 	    SafeRelease(&mTextFormat);
872 	    mTextFormat = item.pTextFormat;
873 	    mFontWeight = item.fontWeight;
874 	    mFontStyle = item.fontStyle;
875 	    mFallbackDC = false;
876 	}
877 	else
878 	    mFallbackDC = true;
879 	return;
880     }
881 
882     HRESULT hr = E_FAIL;
883     LOGFONTW lf;
884     if (GetObjectW(hFont, sizeof(lf), &lf))
885 	hr = SetFontByLOGFONT(lf);
886 
887     item.hFont = hFont;
888     if (SUCCEEDED(hr))
889     {
890 	item.pTextFormat = mTextFormat;
891 	item.fontWeight = mFontWeight;
892 	item.fontStyle = mFontStyle;
893 	mFallbackDC = false;
894     }
895     else
896 	mFallbackDC = true;
897     mFontCache.put(item);
898 }
899 
900     void
901 DWriteContext::Rebind()
902 {
903     SafeRelease(&mBitmap);
904 
905     mRT->BindDC(mHDC, &mBindRect);
906     mRT->SetTransform(D2D1::IdentityMatrix());
907 
908     D2D1_BITMAP_PROPERTIES props = {
909 	{DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE},
910 	96.0f, 96.0f
911     };
912     mRT->CreateBitmap(
913 	    D2D1::SizeU(mBindRect.right - mBindRect.left,
914 		mBindRect.bottom - mBindRect.top),
915 	    props, &mBitmap);
916 }
917 
918     void
919 DWriteContext::BindDC(HDC hdc, const RECT *rect)
920 {
921     mHDC = hdc;
922     mBindRect = *rect;
923 
924     if (mRT == NULL)
925 	CreateDeviceResources();
926     else
927     {
928 	Flush();
929 	Rebind();
930     }
931 }
932 
933 extern "C" void redraw_later_clear(void);
934 
935     HRESULT
936 DWriteContext::SetDrawingMode(DrawingMode mode)
937 {
938     HRESULT hr = S_OK;
939 
940     switch (mode)
941     {
942 	default:
943 	case DM_GDI:
944 	    if (mInteropHDC != NULL)
945 	    {
946 		mGDIRT->ReleaseDC(NULL);
947 		mInteropHDC = NULL;
948 	    }
949 	    if (mDrawing)
950 	    {
951 		hr = mRT->EndDraw();
952 		if (hr == (HRESULT)D2DERR_RECREATE_TARGET)
953 		{
954 		    hr = S_OK;
955 		    DiscardDeviceResources();
956 		    CreateDeviceResources();
957 		    redraw_later_clear();
958 		}
959 		mDrawing = false;
960 	    }
961 	    break;
962 
963 	case DM_DIRECTX:
964 	    if (mInteropHDC != NULL)
965 	    {
966 		mGDIRT->ReleaseDC(NULL);
967 		mInteropHDC = NULL;
968 	    }
969 	    else if (mDrawing == false)
970 	    {
971 		CreateDeviceResources();
972 		mRT->BeginDraw();
973 		mDrawing = true;
974 	    }
975 	    break;
976 
977 	case DM_INTEROP:
978 	    if (mDrawing == false)
979 	    {
980 		CreateDeviceResources();
981 		mRT->BeginDraw();
982 		mDrawing = true;
983 	    }
984 	    if (mInteropHDC == NULL)
985 		hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC);
986 	    break;
987     }
988     mDMode = mode;
989     return hr;
990 }
991 
992     ID2D1Brush*
993 DWriteContext::SolidBrush(COLORREF color)
994 {
995     mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 |
996 		UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color))));
997     return mBrush;
998 }
999 
1000     void
1001 DWriteContext::DrawText(const WCHAR *text, int len,
1002 	int x, int y, int w, int h, int cellWidth, COLORREF color,
1003 	UINT fuOptions, const RECT *lprc, const INT *lpDx)
1004 {
1005     if (mFallbackDC)
1006     {
1007 	// Fall back to GDI rendering.
1008 	HRESULT hr = SetDrawingMode(DM_INTEROP);
1009 	if (SUCCEEDED(hr))
1010 	{
1011 	    HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT);
1012 	    HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont);
1013 	    ::SetTextColor(mInteropHDC, color);
1014 	    ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC));
1015 	    ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx);
1016 	    ::SelectObject(mInteropHDC, hOldFont);
1017 	}
1018 	return;
1019     }
1020 
1021     HRESULT hr;
1022     IDWriteTextLayout *textLayout = NULL;
1023 
1024     SetDrawingMode(DM_DIRECTX);
1025 
1026     hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
1027 	    FLOAT(w), FLOAT(h), &textLayout);
1028 
1029     if (SUCCEEDED(hr))
1030     {
1031 	DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
1032 	textLayout->SetFontWeight(mFontWeight, textRange);
1033 	textLayout->SetFontStyle(mFontStyle, textRange);
1034 
1035 	TextRenderer renderer(this);
1036 	TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
1037 	textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y));
1038     }
1039 
1040     SafeRelease(&textLayout);
1041 }
1042 
1043     void
1044 DWriteContext::FillRect(const RECT *rc, COLORREF color)
1045 {
1046     if (mDMode == DM_INTEROP)
1047     {
1048 	// GDI functions are used before this call.  Keep using GDI.
1049 	// (Switching to Direct2D causes terrible slowdown.)
1050 	HBRUSH hbr = ::CreateSolidBrush(color);
1051 	::FillRect(mInteropHDC, rc, hbr);
1052 	::DeleteObject(HGDIOBJ(hbr));
1053     }
1054     else
1055     {
1056 	SetDrawingMode(DM_DIRECTX);
1057 	mRT->FillRectangle(
1058 		D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top),
1059 		    FLOAT(rc->right), FLOAT(rc->bottom)),
1060 		SolidBrush(color));
1061     }
1062 }
1063 
1064     void
1065 DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color)
1066 {
1067     if (mDMode == DM_INTEROP)
1068     {
1069 	// GDI functions are used before this call.  Keep using GDI.
1070 	// (Switching to Direct2D causes terrible slowdown.)
1071 	HPEN hpen = ::CreatePen(PS_SOLID, 1, color);
1072 	HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen));
1073 	::MoveToEx(mInteropHDC, x1, y1, NULL);
1074 	::LineTo(mInteropHDC, x2, y2);
1075 	::SelectObject(mInteropHDC, old_pen);
1076 	::DeleteObject(HGDIOBJ(hpen));
1077     }
1078     else
1079     {
1080 	SetDrawingMode(DM_DIRECTX);
1081 	mRT->DrawLine(
1082 		D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f),
1083 		D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f),
1084 		SolidBrush(color));
1085     }
1086 }
1087 
1088     void
1089 DWriteContext::SetPixel(int x, int y, COLORREF color)
1090 {
1091     if (mDMode == DM_INTEROP)
1092     {
1093 	// GDI functions are used before this call.  Keep using GDI.
1094 	// (Switching to Direct2D causes terrible slowdown.)
1095 	::SetPixel(mInteropHDC, x, y, color);
1096     }
1097     else
1098     {
1099 	SetDrawingMode(DM_DIRECTX);
1100 	// Direct2D doesn't have SetPixel API.  Use DrawLine instead.
1101 	mRT->DrawLine(
1102 		D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f),
1103 		D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f),
1104 		SolidBrush(color));
1105     }
1106 }
1107 
1108     void
1109 DWriteContext::Scroll(int x, int y, const RECT *rc)
1110 {
1111     SetDrawingMode(DM_DIRECTX);
1112     mRT->Flush();
1113 
1114     D2D1_RECT_U srcRect;
1115     D2D1_POINT_2U destPoint;
1116     if (x >= 0)
1117     {
1118 	srcRect.left = rc->left;
1119 	srcRect.right = rc->right - x;
1120 	destPoint.x = rc->left + x;
1121     }
1122     else
1123     {
1124 	srcRect.left = rc->left - x;
1125 	srcRect.right = rc->right;
1126 	destPoint.x = rc->left;
1127     }
1128     if (y >= 0)
1129     {
1130 	srcRect.top = rc->top;
1131 	srcRect.bottom = rc->bottom - y;
1132 	destPoint.y = rc->top + y;
1133     }
1134     else
1135     {
1136 	srcRect.top = rc->top - y;
1137 	srcRect.bottom = rc->bottom;
1138 	destPoint.y = rc->top;
1139     }
1140     mBitmap->CopyFromRenderTarget(&destPoint, mRT, &srcRect);
1141 
1142     D2D1_RECT_F destRect = {
1143 	    FLOAT(destPoint.x), FLOAT(destPoint.y),
1144 	    FLOAT(destPoint.x + srcRect.right - srcRect.left),
1145 	    FLOAT(destPoint.y + srcRect.bottom - srcRect.top)
1146     };
1147     mRT->DrawBitmap(mBitmap, destRect, 1.0F,
1148 	    D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, destRect);
1149 }
1150 
1151     void
1152 DWriteContext::Flush()
1153 {
1154     SetDrawingMode(DM_GDI);
1155 }
1156 
1157     void
1158 DWriteContext::SetRenderingParams(
1159 	const DWriteRenderingParams *params)
1160 {
1161     if (mDWriteFactory == NULL)
1162 	return;
1163 
1164     IDWriteRenderingParams *renderingParams = NULL;
1165     D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =
1166 	D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
1167     HRESULT hr;
1168     if (params != NULL)
1169     {
1170 	hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,
1171 		params->enhancedContrast, params->clearTypeLevel,
1172 		ToPixelGeometry(params->pixelGeometry),
1173 		ToRenderingMode(params->renderingMode), &renderingParams);
1174 	textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);
1175     }
1176     else
1177 	hr = mDWriteFactory->CreateRenderingParams(&renderingParams);
1178     if (SUCCEEDED(hr) && renderingParams != NULL)
1179     {
1180 	SafeRelease(&mRenderingParams);
1181 	mRenderingParams = renderingParams;
1182 	mTextAntialiasMode = textAntialiasMode;
1183 
1184 	Flush();
1185 	mRT->SetTextRenderingParams(mRenderingParams);
1186 	mRT->SetTextAntialiasMode(mTextAntialiasMode);
1187     }
1188 }
1189 
1190     DWriteRenderingParams *
1191 DWriteContext::GetRenderingParams(
1192 	DWriteRenderingParams *params)
1193 {
1194     if (params != NULL && mRenderingParams != NULL)
1195     {
1196 	params->gamma = mRenderingParams->GetGamma();
1197 	params->enhancedContrast = mRenderingParams->GetEnhancedContrast();
1198 	params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();
1199 	params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());
1200 	params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());
1201 	params->textAntialiasMode = mTextAntialiasMode;
1202     }
1203     return params;
1204 }
1205 
1206 ////////////////////////////////////////////////////////////////////////////
1207 // PUBLIC C INTERFACES
1208 
1209     void
1210 DWrite_Init(void)
1211 {
1212 #ifdef DYNAMIC_DIRECTX
1213     // Load libraries.
1214     hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll"));
1215     hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll"));
1216     if (hD2D1DLL == NULL || hDWriteDLL == NULL)
1217     {
1218 	DWrite_Final();
1219 	return;
1220     }
1221     // Get address of procedures.
1222     pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(
1223 	    GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");
1224     pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,
1225 	    "D2D1CreateFactory");
1226     pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,
1227 	    "DWriteCreateFactory");
1228 #endif
1229 }
1230 
1231     void
1232 DWrite_Final(void)
1233 {
1234 #ifdef DYNAMIC_DIRECTX
1235     pGetUserDefaultLocaleName = NULL;
1236     pD2D1CreateFactory = NULL;
1237     pDWriteCreateFactory = NULL;
1238     unload(hDWriteDLL);
1239     unload(hD2D1DLL);
1240 #endif
1241 }
1242 
1243     DWriteContext *
1244 DWriteContext_Open(void)
1245 {
1246 #ifdef DYNAMIC_DIRECTX
1247     if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL
1248 	    || pDWriteCreateFactory == NULL)
1249 	return NULL;
1250 #endif
1251     return new DWriteContext();
1252 }
1253 
1254     void
1255 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect)
1256 {
1257     if (ctx != NULL)
1258 	ctx->BindDC(hdc, rect);
1259 }
1260 
1261     void
1262 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
1263 {
1264     if (ctx != NULL)
1265 	ctx->SetFont(hFont);
1266 }
1267 
1268     void
1269 DWriteContext_DrawText(
1270 	DWriteContext *ctx,
1271 	const WCHAR *text,
1272 	int len,
1273 	int x,
1274 	int y,
1275 	int w,
1276 	int h,
1277 	int cellWidth,
1278 	COLORREF color,
1279 	UINT fuOptions,
1280 	const RECT *lprc,
1281 	const INT *lpDx)
1282 {
1283     if (ctx != NULL)
1284 	ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
1285 		fuOptions, lprc, lpDx);
1286 }
1287 
1288     void
1289 DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color)
1290 {
1291     if (ctx != NULL)
1292 	ctx->FillRect(rc, color);
1293 }
1294 
1295     void
1296 DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2,
1297 	COLORREF color)
1298 {
1299     if (ctx != NULL)
1300 	ctx->DrawLine(x1, y1, x2, y2, color);
1301 }
1302 
1303     void
1304 DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color)
1305 {
1306     if (ctx != NULL)
1307 	ctx->SetPixel(x, y, color);
1308 }
1309 
1310     void
1311 DWriteContext_Scroll(DWriteContext *ctx, int x, int y, const RECT *rc)
1312 {
1313     if (ctx != NULL)
1314 	ctx->Scroll(x, y, rc);
1315 }
1316 
1317     void
1318 DWriteContext_Flush(DWriteContext *ctx)
1319 {
1320     if (ctx != NULL)
1321 	ctx->Flush();
1322 }
1323 
1324     void
1325 DWriteContext_Close(DWriteContext *ctx)
1326 {
1327     delete ctx;
1328 }
1329 
1330     void
1331 DWriteContext_SetRenderingParams(
1332 	DWriteContext *ctx,
1333 	const DWriteRenderingParams *params)
1334 {
1335     if (ctx != NULL)
1336 	ctx->SetRenderingParams(params);
1337 }
1338 
1339     DWriteRenderingParams *
1340 DWriteContext_GetRenderingParams(
1341 	DWriteContext *ctx,
1342 	DWriteRenderingParams *params)
1343 {
1344     if (ctx != NULL)
1345 	return ctx->GetRenderingParams(params);
1346     else
1347 	return NULL;
1348 }
1349