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