blob: 83acdab1ad959647c922ddf70e79178442b532ff [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
Bram Moolenaar7f88b652017-12-14 13:15:19 +010041#ifdef FEAT_DIRECTX_COLOR_EMOJI
42# include <dwrite_2.h>
43#else
44# include <dwrite.h>
45#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020046
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
Bram Moolenaarcc6cf9b2016-03-19 20:51:35 +010055#if (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L)
56# define FINAL final
57#else
58# define FINAL
59#endif
60
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020061#ifdef DYNAMIC_DIRECTX
62extern "C" HINSTANCE vimLoadLib(char *name);
63
64typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int);
65typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE,
66 REFIID, const D2D1_FACTORY_OPTIONS *, void **);
67typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE,
68 REFIID, IUnknown **);
69
70static HINSTANCE hD2D1DLL = NULL;
71static HINSTANCE hDWriteDLL = NULL;
72
73static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL;
74static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL;
75static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL;
76
77#define GetUserDefaultLocaleName (*pGetUserDefaultLocaleName)
78#define D2D1CreateFactory (*pD2D1CreateFactory)
79#define DWriteCreateFactory (*pDWriteCreateFactory)
80
81 static void
82unload(HINSTANCE &hinst)
83{
84 if (hinst != NULL)
85 {
86 FreeLibrary(hinst);
87 hinst = NULL;
88 }
89}
90#endif // DYNAMIC_DIRECTX
91
92template <class T> inline void SafeRelease(T **ppT)
93{
94 if (*ppT)
95 {
96 (*ppT)->Release();
97 *ppT = NULL;
98 }
99}
100
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200101 static DWRITE_PIXEL_GEOMETRY
102ToPixelGeometry(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
117ToInt(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
133ToRenderingMode(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
156ToTextAntialiasMode(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
173ToInt(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
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100196class FontCache {
197public:
198 struct Item {
199 HFONT hFont;
200 IDWriteTextFormat* pTextFormat;
201 DWRITE_FONT_WEIGHT fontWeight;
202 DWRITE_FONT_STYLE fontStyle;
203 Item() : hFont(NULL), pTextFormat(NULL) {}
204 };
205
206private:
207 int mSize;
208 Item *mItems;
209
210public:
211 FontCache(int size = 2) :
212 mSize(size),
213 mItems(new Item[size])
214 {
215 }
216
217 ~FontCache()
218 {
219 for (int i = 0; i < mSize; ++i)
220 SafeRelease(&mItems[i].pTextFormat);
221 delete[] mItems;
222 }
223
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
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);
Bram Moolenaar1b3e0722020-12-08 21:12:40 +0100242 if (item.pTextFormat != NULL)
243 item.pTextFormat->AddRef();
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100244 }
245 mItems[n] = item;
246 slide(n);
247 }
248
249private:
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
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
Bram Moolenaar92467d32017-12-05 13:22:16 +0100271enum DrawingMode {
272 DM_GDI = 0,
273 DM_DIRECTX = 1,
274 DM_INTEROP = 2,
275};
276
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100277struct DWriteContext {
278 HDC mHDC;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100279 RECT mBindRect;
280 DrawingMode mDMode;
281 HDC mInteropHDC;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100282 bool mDrawing;
283 bool mFallbackDC;
284
285 ID2D1Factory *mD2D1Factory;
286
287 ID2D1DCRenderTarget *mRT;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100288 ID2D1GdiInteropRenderTarget *mGDIRT;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100289 ID2D1SolidColorBrush *mBrush;
Bram Moolenaara338adc2018-01-31 20:51:47 +0100290 ID2D1Bitmap *mBitmap;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100291
292 IDWriteFactory *mDWriteFactory;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100293#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100294 IDWriteFactory2 *mDWriteFactory2;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100295#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100296
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
Bram Moolenaar92467d32017-12-05 13:22:16 +0100313 HRESULT CreateDeviceResources();
314
315 void DiscardDeviceResources();
316
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100317 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
318 IDWriteTextFormat **ppTextFormat);
319
320 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
321
322 void SetFont(HFONT hFont);
323
Bram Moolenaara338adc2018-01-31 20:51:47 +0100324 void Rebind();
325
Bram Moolenaar92467d32017-12-05 13:22:16 +0100326 void BindDC(HDC hdc, const RECT *rect);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100327
Bram Moolenaar92467d32017-12-05 13:22:16 +0100328 HRESULT SetDrawingMode(DrawingMode mode);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100329
330 ID2D1Brush* SolidBrush(COLORREF color);
331
Bram Moolenaar92467d32017-12-05 13:22:16 +0100332 void DrawText(const WCHAR *text, int len,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100333 int x, int y, int w, int h, int cellWidth, COLORREF color,
Bram Moolenaar92467d32017-12-05 13:22:16 +0100334 UINT fuOptions, const RECT *lprc, const INT *lpDx);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100335
Bram Moolenaar92467d32017-12-05 13:22:16 +0100336 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);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100341
Bram Moolenaara338adc2018-01-31 20:51:47 +0100342 void Scroll(int x, int y, const RECT *rc);
343
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100344 void Flush();
345
346 void SetRenderingParams(
347 const DWriteRenderingParams *params);
348
349 DWriteRenderingParams *GetRenderingParams(
350 DWriteRenderingParams *params);
351};
352
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200353class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
354{
355private:
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100356 FLOAT &mAccum;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200357 FLOAT mDelta;
358 FLOAT *mAdjustedAdvances;
359
360public:
361 AdjustedGlyphRun(
362 const DWRITE_GLYPH_RUN *glyphRun,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100363 FLOAT cellWidth,
364 FLOAT &accum) :
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200365 DWRITE_GLYPH_RUN(*glyphRun),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100366 mAccum(accum),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200367 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
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100381 ~AdjustedGlyphRun()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200382 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100383 mAccum += mDelta;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200384 delete[] mAdjustedAdvances;
385 }
386
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200387 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
388 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100389 int cellCount = int(floor(value / cellWidth + 0.5f));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200390 if (cellCount < 1)
391 cellCount = 1;
392 return cellCount * cellWidth;
393 }
394};
395
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100396struct TextRendererContext {
397 // const fields.
398 COLORREF color;
399 FLOAT cellWidth;
400
401 // working fields.
402 FLOAT offsetX;
403};
404
405class TextRenderer FINAL : public IDWriteTextRenderer
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200406{
407public:
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100408 TextRenderer(
409 DWriteContext* pDWC) :
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200410 cRefCount_(0),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100411 pDWC_(pDWC)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200412 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200413 AddRef();
414 }
415
Bram Moolenaaredb4f2b2016-02-27 15:27:23 +0100416 // add "virtual" to avoid a compiler warning
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100417 virtual ~TextRenderer()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200418 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200419 }
420
421 IFACEMETHOD(IsPixelSnappingDisabled)(
422 __maybenull void* clientDrawingContext,
423 __out BOOL* isDisabled)
424 {
425 *isDisabled = FALSE;
426 return S_OK;
427 }
428
429 IFACEMETHOD(GetCurrentTransform)(
430 __maybenull void* clientDrawingContext,
431 __out DWRITE_MATRIX* transform)
432 {
Bram Moolenaaredb4f2b2016-02-27 15:27:23 +0100433 // forward the render target's transform
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100434 pDWC_->mRT->GetTransform(
435 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200436 return S_OK;
437 }
438
439 IFACEMETHOD(GetPixelsPerDip)(
440 __maybenull void* clientDrawingContext,
441 __out FLOAT* pixelsPerDip)
442 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100443 float dpiX, unused;
444 pDWC_->mRT->GetDpi(&dpiX, &unused);
445 *pixelsPerDip = dpiX / 96.0f;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200446 return S_OK;
447 }
448
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200449 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
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
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
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100481 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
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100496#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100497 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 }
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100533#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100534
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
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200546public:
547 IFACEMETHOD_(unsigned long, AddRef) ()
548 {
549 return InterlockedIncrement(&cRefCount_);
550 }
551
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
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
589private:
Bram Moolenaar0106b4b2014-08-07 13:55:10 +0200590 long cRefCount_;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100591 DWriteContext* pDWC_;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200592};
593
594DWriteContext::DWriteContext() :
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100595 mHDC(NULL),
Bram Moolenaar92467d32017-12-05 13:22:16 +0100596 mBindRect(),
597 mDMode(DM_GDI),
598 mInteropHDC(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200599 mDrawing(false),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100600 mFallbackDC(false),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200601 mD2D1Factory(NULL),
602 mRT(NULL),
Bram Moolenaar92467d32017-12-05 13:22:16 +0100603 mGDIRT(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200604 mBrush(NULL),
Bram Moolenaara338adc2018-01-31 20:51:47 +0100605 mBitmap(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200606 mDWriteFactory(NULL),
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100607#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100608 mDWriteFactory2(NULL),
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100609#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200610 mGdiInterop(NULL),
611 mRenderingParams(NULL),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100612 mFontCache(8),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200613 mTextFormat(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200614 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
615 mFontStyle(DWRITE_FONT_STYLE_NORMAL),
616 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
617{
618 HRESULT hr;
619
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200620 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))
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200626 {
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
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100635#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200636 if (SUCCEEDED(hr))
637 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100638 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 }
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100644#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100645
646 if (SUCCEEDED(hr))
647 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200648 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
660DWriteContext::~DWriteContext()
661{
662 SafeRelease(&mTextFormat);
663 SafeRelease(&mRenderingParams);
664 SafeRelease(&mGdiInterop);
665 SafeRelease(&mDWriteFactory);
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100666#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100667 SafeRelease(&mDWriteFactory2);
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100668#endif
Bram Moolenaara338adc2018-01-31 20:51:47 +0100669 SafeRelease(&mBitmap);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200670 SafeRelease(&mBrush);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100671 SafeRelease(&mGDIRT);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200672 SafeRelease(&mRT);
673 SafeRelease(&mD2D1Factory);
674}
675
676 HRESULT
Bram Moolenaar92467d32017-12-05 13:22:16 +0100677DWriteContext::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))
Bram Moolenaara338adc2018-01-31 20:51:47 +0100712 Rebind();
Bram Moolenaar92467d32017-12-05 13:22:16 +0100713
714 return hr;
715}
716
717 void
718DWriteContext::DiscardDeviceResources()
719{
Bram Moolenaara338adc2018-01-31 20:51:47 +0100720 SafeRelease(&mBitmap);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100721 SafeRelease(&mBrush);
722 SafeRelease(&mGDIRT);
723 SafeRelease(&mRT);
724}
725
726 HRESULT
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100727DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
728 IDWriteTextFormat **ppTextFormat)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200729{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100730 // 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 +0200731 HRESULT hr = S_OK;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100732 IDWriteTextFormat *pTextFormat = NULL;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200733
734 IDWriteFont *font = NULL;
735 IDWriteFontFamily *fontFamily = NULL;
736 IDWriteLocalizedStrings *localizedFamilyNames = NULL;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100737 float fontSize = 0;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200738
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 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100770 // Use lfHeight of the LOGFONT as font size.
771 fontSize = float(logFont.lfHeight);
772
773 if (fontSize < 0)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200774 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100775 // 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);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200784
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100785 // 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;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200790
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100791 // Divide the font size by the cell height to get the font em
792 // size.
793 fontSize /= cellHeight;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200794 }
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,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100818 &pTextFormat);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200819 }
820
821 if (SUCCEEDED(hr))
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100822 hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
823
824 if (SUCCEEDED(hr))
825 hr = pTextFormat->SetParagraphAlignment(
Bram Moolenaar1f271ef2019-03-16 15:24:42 +0100826 DWRITE_PARAGRAPH_ALIGNMENT_FAR);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100827
828 if (SUCCEEDED(hr))
829 hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200830
831 SafeRelease(&localizedFamilyNames);
832 SafeRelease(&fontFamily);
833 SafeRelease(&font);
834
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100835 if (SUCCEEDED(hr))
836 *ppTextFormat = pTextFormat;
837 else
838 SafeRelease(&pTextFormat);
839
840 return hr;
841}
842
843 HRESULT
844DWriteContext::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
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200860 return hr;
861}
862
863 void
864DWriteContext::SetFont(HFONT hFont)
865{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100866 FontCache::Item item;
867 if (mFontCache.get(hFont, item))
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200868 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100869 if (item.pTextFormat != NULL)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200870 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100871 item.pTextFormat->AddRef();
872 SafeRelease(&mTextFormat);
873 mTextFormat = item.pTextFormat;
874 mFontWeight = item.fontWeight;
875 mFontStyle = item.fontStyle;
876 mFallbackDC = false;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200877 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100878 else
879 mFallbackDC = true;
880 return;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200881 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100882
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;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100894 mFallbackDC = false;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100895 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100896 else
897 mFallbackDC = true;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100898 mFontCache.put(item);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200899}
900
901 void
Bram Moolenaara338adc2018-01-31 20:51:47 +0100902DWriteContext::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
Bram Moolenaar92467d32017-12-05 13:22:16 +0100920DWriteContext::BindDC(HDC hdc, const RECT *rect)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200921{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100922 mHDC = hdc;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100923 mBindRect = *rect;
Bram Moolenaara338adc2018-01-31 20:51:47 +0100924
925 if (mRT == NULL)
926 CreateDeviceResources();
927 else
928 {
929 Flush();
930 Rebind();
931 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200932}
933
Bram Moolenaar3dd174a2019-03-25 22:48:18 +0100934extern "C" void redraw_later_clear(void);
935
Bram Moolenaar92467d32017-12-05 13:22:16 +0100936 HRESULT
937DWriteContext::SetDrawingMode(DrawingMode mode)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200938{
Bram Moolenaar92467d32017-12-05 13:22:16 +0100939 HRESULT hr = S_OK;
940
941 switch (mode)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100942 {
Bram Moolenaar92467d32017-12-05 13:22:16 +0100943 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();
Bram Moolenaarf653a6b2019-05-05 13:20:02 +0200953 if (hr == (HRESULT)D2DERR_RECREATE_TARGET)
Bram Moolenaar92467d32017-12-05 13:22:16 +0100954 {
955 hr = S_OK;
956 DiscardDeviceResources();
957 CreateDeviceResources();
Bram Moolenaar3dd174a2019-03-25 22:48:18 +0100958 redraw_later_clear();
Bram Moolenaar92467d32017-12-05 13:22:16 +0100959 }
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;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100988 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100989 mDMode = mode;
990 return hr;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100991}
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200992
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100993 ID2D1Brush*
994DWriteContext::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
Bram Moolenaar92467d32017-12-05 13:22:16 +01001002DWriteContext::DrawText(const WCHAR *text, int len,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001003 int x, int y, int w, int h, int cellWidth, COLORREF color,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001004 UINT fuOptions, const RECT *lprc, const INT *lpDx)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001005{
1006 if (mFallbackDC)
1007 {
Bram Moolenaar92467d32017-12-05 13:22:16 +01001008 // 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 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001019 return;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001020 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001021
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001022 HRESULT hr;
1023 IDWriteTextLayout *textLayout = NULL;
1024
Bram Moolenaar92467d32017-12-05 13:22:16 +01001025 SetDrawingMode(DM_DIRECTX);
1026
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001027 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
1028 FLOAT(w), FLOAT(h), &textLayout);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001029
1030 if (SUCCEEDED(hr))
1031 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001032 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
1033 textLayout->SetFontWeight(mFontWeight, textRange);
1034 textLayout->SetFontStyle(mFontStyle, textRange);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001035
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001036 TextRenderer renderer(this);
1037 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
Bram Moolenaar60ebd522019-03-21 20:50:12 +01001038 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001039 }
1040
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001041 SafeRelease(&textLayout);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001042}
1043
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001044 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001045DWriteContext::FillRect(const RECT *rc, COLORREF color)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001046{
Bram Moolenaar92467d32017-12-05 13:22:16 +01001047 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
1066DWriteContext::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
1090DWriteContext::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 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001107}
1108
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001109 void
Bram Moolenaara338adc2018-01-31 20:51:47 +01001110DWriteContext::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
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001153DWriteContext::Flush()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001154{
Bram Moolenaar92467d32017-12-05 13:22:16 +01001155 SetDrawingMode(DM_GDI);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001156}
1157
1158 void
1159DWriteContext::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;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001184
1185 Flush();
1186 mRT->SetTextRenderingParams(mRenderingParams);
1187 mRT->SetTextAntialiasMode(mTextAntialiasMode);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001188 }
1189}
1190
1191 DWriteRenderingParams *
1192DWriteContext::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
1211DWrite_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
1233DWrite_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 *
1245DWriteContext_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
Bram Moolenaar92467d32017-12-05 13:22:16 +01001256DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001257{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001258 if (ctx != NULL)
1259 ctx->BindDC(hdc, rect);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001260}
1261
1262 void
1263DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
1264{
1265 if (ctx != NULL)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001266 ctx->SetFont(hFont);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001267}
1268
1269 void
1270DWriteContext_DrawText(
1271 DWriteContext *ctx,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001272 const WCHAR *text,
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001273 int len,
1274 int x,
1275 int y,
1276 int w,
1277 int h,
1278 int cellWidth,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001279 COLORREF color,
1280 UINT fuOptions,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001281 const RECT *lprc,
1282 const INT *lpDx)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001283{
1284 if (ctx != NULL)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001285 ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
1286 fuOptions, lprc, lpDx);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001287}
1288
1289 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001290DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001291{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001292 if (ctx != NULL)
1293 ctx->FillRect(rc, color);
1294}
1295
1296 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001297DWriteContext_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
1305DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color)
1306{
1307 if (ctx != NULL)
1308 ctx->SetPixel(x, y, color);
1309}
1310
1311 void
Bram Moolenaara338adc2018-01-31 20:51:47 +01001312DWriteContext_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
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001319DWriteContext_Flush(DWriteContext *ctx)
1320{
1321 if (ctx != NULL)
1322 ctx->Flush();
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001323}
1324
1325 void
1326DWriteContext_Close(DWriteContext *ctx)
1327{
1328 delete ctx;
1329}
1330
1331 void
1332DWriteContext_SetRenderingParams(
1333 DWriteContext *ctx,
1334 const DWriteRenderingParams *params)
1335{
1336 if (ctx != NULL)
1337 ctx->SetRenderingParams(params);
1338}
1339
1340 DWriteRenderingParams *
1341DWriteContext_GetRenderingParams(
1342 DWriteContext *ctx,
1343 DWriteRenderingParams *params)
1344{
1345 if (ctx != NULL)
1346 return ctx->GetRenderingParams(params);
1347 else
1348 return NULL;
1349}