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