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