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