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