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