blob: 8767dc1af9c2c1032c1345862f59e4deb4c6c2d7 [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);
242 item.pTextFormat->AddRef();
243 }
244 mItems[n] = item;
245 slide(n);
246 }
247
248private:
249 int find(HFONT hFont)
250 {
251 for (int i = 0; i < mSize; ++i)
252 {
253 if (mItems[i].hFont == hFont)
254 return i;
255 }
256 return -1;
257 }
258
259 void slide(int nextTop)
260 {
261 if (nextTop == 0)
262 return;
263 Item tmp = mItems[nextTop];
264 for (int i = nextTop - 1; i >= 0; --i)
265 mItems[i + 1] = mItems[i];
266 mItems[0] = tmp;
267 }
268};
269
Bram Moolenaar92467d32017-12-05 13:22:16 +0100270enum DrawingMode {
271 DM_GDI = 0,
272 DM_DIRECTX = 1,
273 DM_INTEROP = 2,
274};
275
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100276struct DWriteContext {
277 HDC mHDC;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100278 RECT mBindRect;
279 DrawingMode mDMode;
280 HDC mInteropHDC;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100281 bool mDrawing;
282 bool mFallbackDC;
283
284 ID2D1Factory *mD2D1Factory;
285
286 ID2D1DCRenderTarget *mRT;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100287 ID2D1GdiInteropRenderTarget *mGDIRT;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100288 ID2D1SolidColorBrush *mBrush;
Bram Moolenaara338adc2018-01-31 20:51:47 +0100289 ID2D1Bitmap *mBitmap;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100290
291 IDWriteFactory *mDWriteFactory;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100292#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100293 IDWriteFactory2 *mDWriteFactory2;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100294#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100295
296 IDWriteGdiInterop *mGdiInterop;
297 IDWriteRenderingParams *mRenderingParams;
298
299 FontCache mFontCache;
300 IDWriteTextFormat *mTextFormat;
301 DWRITE_FONT_WEIGHT mFontWeight;
302 DWRITE_FONT_STYLE mFontStyle;
303
304 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
305
306 // METHODS
307
308 DWriteContext();
309
310 virtual ~DWriteContext();
311
Bram Moolenaar92467d32017-12-05 13:22:16 +0100312 HRESULT CreateDeviceResources();
313
314 void DiscardDeviceResources();
315
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100316 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
317 IDWriteTextFormat **ppTextFormat);
318
319 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
320
321 void SetFont(HFONT hFont);
322
Bram Moolenaara338adc2018-01-31 20:51:47 +0100323 void Rebind();
324
Bram Moolenaar92467d32017-12-05 13:22:16 +0100325 void BindDC(HDC hdc, const RECT *rect);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100326
Bram Moolenaar92467d32017-12-05 13:22:16 +0100327 HRESULT SetDrawingMode(DrawingMode mode);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100328
329 ID2D1Brush* SolidBrush(COLORREF color);
330
Bram Moolenaar92467d32017-12-05 13:22:16 +0100331 void DrawText(const WCHAR *text, int len,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100332 int x, int y, int w, int h, int cellWidth, COLORREF color,
Bram Moolenaar92467d32017-12-05 13:22:16 +0100333 UINT fuOptions, const RECT *lprc, const INT *lpDx);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100334
Bram Moolenaar92467d32017-12-05 13:22:16 +0100335 void FillRect(const RECT *rc, COLORREF color);
336
337 void DrawLine(int x1, int y1, int x2, int y2, COLORREF color);
338
339 void SetPixel(int x, int y, COLORREF color);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100340
Bram Moolenaara338adc2018-01-31 20:51:47 +0100341 void Scroll(int x, int y, const RECT *rc);
342
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100343 void Flush();
344
345 void SetRenderingParams(
346 const DWriteRenderingParams *params);
347
348 DWriteRenderingParams *GetRenderingParams(
349 DWriteRenderingParams *params);
350};
351
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200352class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
353{
354private:
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100355 FLOAT &mAccum;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200356 FLOAT mDelta;
357 FLOAT *mAdjustedAdvances;
358
359public:
360 AdjustedGlyphRun(
361 const DWRITE_GLYPH_RUN *glyphRun,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100362 FLOAT cellWidth,
363 FLOAT &accum) :
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200364 DWRITE_GLYPH_RUN(*glyphRun),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100365 mAccum(accum),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200366 mDelta(0.0f),
367 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
368 {
369 assert(cellWidth != 0.0f);
370 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
371 {
372 FLOAT orig = glyphRun->glyphAdvances[i];
373 FLOAT adjusted = adjustToCell(orig, cellWidth);
374 mAdjustedAdvances[i] = adjusted;
375 mDelta += adjusted - orig;
376 }
377 glyphAdvances = mAdjustedAdvances;
378 }
379
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100380 ~AdjustedGlyphRun()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200381 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100382 mAccum += mDelta;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200383 delete[] mAdjustedAdvances;
384 }
385
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200386 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
387 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100388 int cellCount = int(floor(value / cellWidth + 0.5f));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200389 if (cellCount < 1)
390 cellCount = 1;
391 return cellCount * cellWidth;
392 }
393};
394
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100395struct TextRendererContext {
396 // const fields.
397 COLORREF color;
398 FLOAT cellWidth;
399
400 // working fields.
401 FLOAT offsetX;
402};
403
404class TextRenderer FINAL : public IDWriteTextRenderer
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200405{
406public:
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100407 TextRenderer(
408 DWriteContext* pDWC) :
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200409 cRefCount_(0),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100410 pDWC_(pDWC)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200411 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200412 AddRef();
413 }
414
Bram Moolenaaredb4f2b2016-02-27 15:27:23 +0100415 // add "virtual" to avoid a compiler warning
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100416 virtual ~TextRenderer()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200417 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200418 }
419
420 IFACEMETHOD(IsPixelSnappingDisabled)(
421 __maybenull void* clientDrawingContext,
422 __out BOOL* isDisabled)
423 {
424 *isDisabled = FALSE;
425 return S_OK;
426 }
427
428 IFACEMETHOD(GetCurrentTransform)(
429 __maybenull void* clientDrawingContext,
430 __out DWRITE_MATRIX* transform)
431 {
Bram Moolenaaredb4f2b2016-02-27 15:27:23 +0100432 // forward the render target's transform
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100433 pDWC_->mRT->GetTransform(
434 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200435 return S_OK;
436 }
437
438 IFACEMETHOD(GetPixelsPerDip)(
439 __maybenull void* clientDrawingContext,
440 __out FLOAT* pixelsPerDip)
441 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100442 float dpiX, unused;
443 pDWC_->mRT->GetDpi(&dpiX, &unused);
444 *pixelsPerDip = dpiX / 96.0f;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200445 return S_OK;
446 }
447
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200448 IFACEMETHOD(DrawUnderline)(
449 __maybenull void* clientDrawingContext,
450 FLOAT baselineOriginX,
451 FLOAT baselineOriginY,
452 __in DWRITE_UNDERLINE const* underline,
453 IUnknown* clientDrawingEffect)
454 {
455 return E_NOTIMPL;
456 }
457
458 IFACEMETHOD(DrawStrikethrough)(
459 __maybenull void* clientDrawingContext,
460 FLOAT baselineOriginX,
461 FLOAT baselineOriginY,
462 __in DWRITE_STRIKETHROUGH const* strikethrough,
463 IUnknown* clientDrawingEffect)
464 {
465 return E_NOTIMPL;
466 }
467
468 IFACEMETHOD(DrawInlineObject)(
469 __maybenull void* clientDrawingContext,
470 FLOAT originX,
471 FLOAT originY,
472 IDWriteInlineObject* inlineObject,
473 BOOL isSideways,
474 BOOL isRightToLeft,
475 IUnknown* clientDrawingEffect)
476 {
477 return E_NOTIMPL;
478 }
479
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100480 IFACEMETHOD(DrawGlyphRun)(
481 __maybenull void* clientDrawingContext,
482 FLOAT baselineOriginX,
483 FLOAT baselineOriginY,
484 DWRITE_MEASURING_MODE measuringMode,
485 __in DWRITE_GLYPH_RUN const* glyphRun,
486 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
487 IUnknown* clientDrawingEffect)
488 {
489 TextRendererContext *context =
490 reinterpret_cast<TextRendererContext*>(clientDrawingContext);
491
492 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth,
493 context->offsetX);
494
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100495#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100496 if (pDWC_->mDWriteFactory2 != NULL)
497 {
498 IDWriteColorGlyphRunEnumerator *enumerator = NULL;
499 HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun(
500 baselineOriginX + context->offsetX,
501 baselineOriginY,
502 &adjustedGlyphRun,
503 NULL,
504 DWRITE_MEASURING_MODE_GDI_NATURAL,
505 NULL,
506 0,
507 &enumerator);
508 if (SUCCEEDED(hr))
509 {
510 // Draw by IDWriteFactory2 for color emoji
511 BOOL hasRun = TRUE;
512 enumerator->MoveNext(&hasRun);
513 while (hasRun)
514 {
515 const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun;
516 enumerator->GetCurrentRun(&colorGlyphRun);
517
518 pDWC_->mBrush->SetColor(colorGlyphRun->runColor);
519 pDWC_->mRT->DrawGlyphRun(
520 D2D1::Point2F(
521 colorGlyphRun->baselineOriginX,
522 colorGlyphRun->baselineOriginY),
523 &colorGlyphRun->glyphRun,
524 pDWC_->mBrush,
525 DWRITE_MEASURING_MODE_NATURAL);
526 enumerator->MoveNext(&hasRun);
527 }
528 SafeRelease(&enumerator);
529 return S_OK;
530 }
531 }
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100532#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100533
534 // Draw by IDWriteFactory (without color emoji)
535 pDWC_->mRT->DrawGlyphRun(
536 D2D1::Point2F(
537 baselineOriginX + context->offsetX,
538 baselineOriginY),
539 &adjustedGlyphRun,
540 pDWC_->SolidBrush(context->color),
541 DWRITE_MEASURING_MODE_NATURAL);
542 return S_OK;
543 }
544
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200545public:
546 IFACEMETHOD_(unsigned long, AddRef) ()
547 {
548 return InterlockedIncrement(&cRefCount_);
549 }
550
551 IFACEMETHOD_(unsigned long, Release) ()
552 {
553 long newCount = InterlockedDecrement(&cRefCount_);
554
555 if (newCount == 0)
556 {
557 delete this;
558 return 0;
559 }
560 return newCount;
561 }
562
563 IFACEMETHOD(QueryInterface)(
564 IID const& riid,
565 void** ppvObject)
566 {
567 if (__uuidof(IDWriteTextRenderer) == riid)
568 {
569 *ppvObject = this;
570 }
571 else if (__uuidof(IDWritePixelSnapping) == riid)
572 {
573 *ppvObject = this;
574 }
575 else if (__uuidof(IUnknown) == riid)
576 {
577 *ppvObject = this;
578 }
579 else
580 {
581 *ppvObject = NULL;
582 return E_FAIL;
583 }
584
585 return S_OK;
586 }
587
588private:
Bram Moolenaar0106b4b2014-08-07 13:55:10 +0200589 long cRefCount_;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100590 DWriteContext* pDWC_;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200591};
592
593DWriteContext::DWriteContext() :
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100594 mHDC(NULL),
Bram Moolenaar92467d32017-12-05 13:22:16 +0100595 mBindRect(),
596 mDMode(DM_GDI),
597 mInteropHDC(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200598 mDrawing(false),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100599 mFallbackDC(false),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200600 mD2D1Factory(NULL),
601 mRT(NULL),
Bram Moolenaar92467d32017-12-05 13:22:16 +0100602 mGDIRT(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200603 mBrush(NULL),
Bram Moolenaara338adc2018-01-31 20:51:47 +0100604 mBitmap(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200605 mDWriteFactory(NULL),
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100606#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100607 mDWriteFactory2(NULL),
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100608#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200609 mGdiInterop(NULL),
610 mRenderingParams(NULL),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100611 mFontCache(8),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200612 mTextFormat(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200613 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
614 mFontStyle(DWRITE_FONT_STYLE_NORMAL),
615 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
616{
617 HRESULT hr;
618
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200619 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
620 __uuidof(ID2D1Factory), NULL,
621 reinterpret_cast<void**>(&mD2D1Factory));
622 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
623
624 if (SUCCEEDED(hr))
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200625 {
626 hr = DWriteCreateFactory(
627 DWRITE_FACTORY_TYPE_SHARED,
628 __uuidof(IDWriteFactory),
629 reinterpret_cast<IUnknown**>(&mDWriteFactory));
630 _RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,
631 mDWriteFactory);
632 }
633
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100634#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200635 if (SUCCEEDED(hr))
636 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100637 DWriteCreateFactory(
638 DWRITE_FACTORY_TYPE_SHARED,
639 __uuidof(IDWriteFactory2),
640 reinterpret_cast<IUnknown**>(&mDWriteFactory2));
641 _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available");
642 }
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100643#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100644
645 if (SUCCEEDED(hr))
646 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200647 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
648 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
649 }
650
651 if (SUCCEEDED(hr))
652 {
653 hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);
654 _RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,
655 mRenderingParams);
656 }
657}
658
659DWriteContext::~DWriteContext()
660{
661 SafeRelease(&mTextFormat);
662 SafeRelease(&mRenderingParams);
663 SafeRelease(&mGdiInterop);
664 SafeRelease(&mDWriteFactory);
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100665#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100666 SafeRelease(&mDWriteFactory2);
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100667#endif
Bram Moolenaara338adc2018-01-31 20:51:47 +0100668 SafeRelease(&mBitmap);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200669 SafeRelease(&mBrush);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100670 SafeRelease(&mGDIRT);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200671 SafeRelease(&mRT);
672 SafeRelease(&mD2D1Factory);
673}
674
675 HRESULT
Bram Moolenaar92467d32017-12-05 13:22:16 +0100676DWriteContext::CreateDeviceResources()
677{
678 HRESULT hr;
679
680 if (mRT != NULL)
681 return S_OK;
682
683 D2D1_RENDER_TARGET_PROPERTIES props = {
684 D2D1_RENDER_TARGET_TYPE_DEFAULT,
685 { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },
686 0, 0,
687 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
688 D2D1_FEATURE_LEVEL_DEFAULT
689 };
690 hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);
691 _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);
692
693 if (SUCCEEDED(hr))
694 {
695 // This always succeeds.
696 mRT->QueryInterface(
697 __uuidof(ID2D1GdiInteropRenderTarget),
698 reinterpret_cast<void**>(&mGDIRT));
699 _RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT);
700 }
701
702 if (SUCCEEDED(hr))
703 {
704 hr = mRT->CreateSolidColorBrush(
705 D2D1::ColorF(D2D1::ColorF::Black),
706 &mBrush);
707 _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);
708 }
709
710 if (SUCCEEDED(hr))
Bram Moolenaara338adc2018-01-31 20:51:47 +0100711 Rebind();
Bram Moolenaar92467d32017-12-05 13:22:16 +0100712
713 return hr;
714}
715
716 void
717DWriteContext::DiscardDeviceResources()
718{
Bram Moolenaara338adc2018-01-31 20:51:47 +0100719 SafeRelease(&mBitmap);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100720 SafeRelease(&mBrush);
721 SafeRelease(&mGDIRT);
722 SafeRelease(&mRT);
723}
724
725 HRESULT
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100726DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
727 IDWriteTextFormat **ppTextFormat)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200728{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100729 // 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 +0200730 HRESULT hr = S_OK;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100731 IDWriteTextFormat *pTextFormat = NULL;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200732
733 IDWriteFont *font = NULL;
734 IDWriteFontFamily *fontFamily = NULL;
735 IDWriteLocalizedStrings *localizedFamilyNames = NULL;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100736 float fontSize = 0;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200737
738 if (SUCCEEDED(hr))
739 {
740 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
741 }
742
743 // Get the font family to which this font belongs.
744 if (SUCCEEDED(hr))
745 {
746 hr = font->GetFontFamily(&fontFamily);
747 }
748
749 // Get the family names. This returns an object that encapsulates one or
750 // more names with the same meaning but in different languages.
751 if (SUCCEEDED(hr))
752 {
753 hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
754 }
755
756 // Get the family name at index zero. If we were going to display the name
757 // we'd want to try to find one that matched the use locale, but for
758 // purposes of creating a text format object any language will do.
759
760 wchar_t familyName[100];
761 if (SUCCEEDED(hr))
762 {
763 hr = localizedFamilyNames->GetString(0, familyName,
764 ARRAYSIZE(familyName));
765 }
766
767 if (SUCCEEDED(hr))
768 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100769 // Use lfHeight of the LOGFONT as font size.
770 fontSize = float(logFont.lfHeight);
771
772 if (fontSize < 0)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200773 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100774 // Negative lfHeight represents the size of the em unit.
775 fontSize = -fontSize;
776 }
777 else
778 {
779 // Positive lfHeight represents the cell height (ascent +
780 // descent).
781 DWRITE_FONT_METRICS fontMetrics;
782 font->GetMetrics(&fontMetrics);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200783
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100784 // Convert the cell height (ascent + descent) from design units
785 // to ems.
786 float cellHeight = static_cast<float>(
787 fontMetrics.ascent + fontMetrics.descent)
788 / fontMetrics.designUnitsPerEm;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200789
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100790 // Divide the font size by the cell height to get the font em
791 // size.
792 fontSize /= cellHeight;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200793 }
794 }
795
796 // The text format includes a locale name. Ideally, this would be the
797 // language of the text, which may or may not be the same as the primary
798 // language of the user. However, for our purposes the user locale will do.
799 wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
800 if (SUCCEEDED(hr))
801 {
802 if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
803 hr = HRESULT_FROM_WIN32(GetLastError());
804 }
805
806 if (SUCCEEDED(hr))
807 {
808 // Create the text format object.
809 hr = mDWriteFactory->CreateTextFormat(
810 familyName,
811 NULL, // no custom font collection
812 font->GetWeight(),
813 font->GetStyle(),
814 font->GetStretch(),
815 fontSize,
816 localeName,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100817 &pTextFormat);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200818 }
819
820 if (SUCCEEDED(hr))
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100821 hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
822
823 if (SUCCEEDED(hr))
824 hr = pTextFormat->SetParagraphAlignment(
Bram Moolenaar1f271ef2019-03-16 15:24:42 +0100825 DWRITE_PARAGRAPH_ALIGNMENT_FAR);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100826
827 if (SUCCEEDED(hr))
828 hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200829
830 SafeRelease(&localizedFamilyNames);
831 SafeRelease(&fontFamily);
832 SafeRelease(&font);
833
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100834 if (SUCCEEDED(hr))
835 *ppTextFormat = pTextFormat;
836 else
837 SafeRelease(&pTextFormat);
838
839 return hr;
840}
841
842 HRESULT
843DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont)
844{
845 HRESULT hr = S_OK;
846 IDWriteTextFormat *pTextFormat = NULL;
847
848 hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat);
849
850 if (SUCCEEDED(hr))
851 {
852 SafeRelease(&mTextFormat);
853 mTextFormat = pTextFormat;
854 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
855 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
856 : DWRITE_FONT_STYLE_NORMAL;
857 }
858
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200859 return hr;
860}
861
862 void
863DWriteContext::SetFont(HFONT hFont)
864{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100865 FontCache::Item item;
866 if (mFontCache.get(hFont, item))
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200867 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100868 if (item.pTextFormat != NULL)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200869 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100870 item.pTextFormat->AddRef();
871 SafeRelease(&mTextFormat);
872 mTextFormat = item.pTextFormat;
873 mFontWeight = item.fontWeight;
874 mFontStyle = item.fontStyle;
875 mFallbackDC = false;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200876 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100877 else
878 mFallbackDC = true;
879 return;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200880 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100881
882 HRESULT hr = E_FAIL;
883 LOGFONTW lf;
884 if (GetObjectW(hFont, sizeof(lf), &lf))
885 hr = SetFontByLOGFONT(lf);
886
887 item.hFont = hFont;
888 if (SUCCEEDED(hr))
889 {
890 item.pTextFormat = mTextFormat;
891 item.fontWeight = mFontWeight;
892 item.fontStyle = mFontStyle;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100893 mFallbackDC = false;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100894 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100895 else
896 mFallbackDC = true;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100897 mFontCache.put(item);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200898}
899
900 void
Bram Moolenaara338adc2018-01-31 20:51:47 +0100901DWriteContext::Rebind()
902{
903 SafeRelease(&mBitmap);
904
905 mRT->BindDC(mHDC, &mBindRect);
906 mRT->SetTransform(D2D1::IdentityMatrix());
907
908 D2D1_BITMAP_PROPERTIES props = {
909 {DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE},
910 96.0f, 96.0f
911 };
912 mRT->CreateBitmap(
913 D2D1::SizeU(mBindRect.right - mBindRect.left,
914 mBindRect.bottom - mBindRect.top),
915 props, &mBitmap);
916}
917
918 void
Bram Moolenaar92467d32017-12-05 13:22:16 +0100919DWriteContext::BindDC(HDC hdc, const RECT *rect)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200920{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100921 mHDC = hdc;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100922 mBindRect = *rect;
Bram Moolenaara338adc2018-01-31 20:51:47 +0100923
924 if (mRT == NULL)
925 CreateDeviceResources();
926 else
927 {
928 Flush();
929 Rebind();
930 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200931}
932
Bram Moolenaar3dd174a2019-03-25 22:48:18 +0100933extern "C" void redraw_later_clear(void);
934
Bram Moolenaar92467d32017-12-05 13:22:16 +0100935 HRESULT
936DWriteContext::SetDrawingMode(DrawingMode mode)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200937{
Bram Moolenaar92467d32017-12-05 13:22:16 +0100938 HRESULT hr = S_OK;
939
940 switch (mode)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100941 {
Bram Moolenaar92467d32017-12-05 13:22:16 +0100942 default:
943 case DM_GDI:
944 if (mInteropHDC != NULL)
945 {
946 mGDIRT->ReleaseDC(NULL);
947 mInteropHDC = NULL;
948 }
949 if (mDrawing)
950 {
951 hr = mRT->EndDraw();
Bram Moolenaarf653a6b2019-05-05 13:20:02 +0200952 if (hr == (HRESULT)D2DERR_RECREATE_TARGET)
Bram Moolenaar92467d32017-12-05 13:22:16 +0100953 {
954 hr = S_OK;
955 DiscardDeviceResources();
956 CreateDeviceResources();
Bram Moolenaar3dd174a2019-03-25 22:48:18 +0100957 redraw_later_clear();
Bram Moolenaar92467d32017-12-05 13:22:16 +0100958 }
959 mDrawing = false;
960 }
961 break;
962
963 case DM_DIRECTX:
964 if (mInteropHDC != NULL)
965 {
966 mGDIRT->ReleaseDC(NULL);
967 mInteropHDC = NULL;
968 }
969 else if (mDrawing == false)
970 {
971 CreateDeviceResources();
972 mRT->BeginDraw();
973 mDrawing = true;
974 }
975 break;
976
977 case DM_INTEROP:
978 if (mDrawing == false)
979 {
980 CreateDeviceResources();
981 mRT->BeginDraw();
982 mDrawing = true;
983 }
984 if (mInteropHDC == NULL)
985 hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC);
986 break;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100987 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100988 mDMode = mode;
989 return hr;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100990}
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200991
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100992 ID2D1Brush*
993DWriteContext::SolidBrush(COLORREF color)
994{
995 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 |
996 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color))));
997 return mBrush;
998}
999
1000 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001001DWriteContext::DrawText(const WCHAR *text, int len,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001002 int x, int y, int w, int h, int cellWidth, COLORREF color,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001003 UINT fuOptions, const RECT *lprc, const INT *lpDx)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001004{
1005 if (mFallbackDC)
1006 {
Bram Moolenaar92467d32017-12-05 13:22:16 +01001007 // Fall back to GDI rendering.
1008 HRESULT hr = SetDrawingMode(DM_INTEROP);
1009 if (SUCCEEDED(hr))
1010 {
1011 HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT);
1012 HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont);
1013 ::SetTextColor(mInteropHDC, color);
1014 ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC));
1015 ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx);
1016 ::SelectObject(mInteropHDC, hOldFont);
1017 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001018 return;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001019 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001020
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001021 HRESULT hr;
1022 IDWriteTextLayout *textLayout = NULL;
1023
Bram Moolenaar92467d32017-12-05 13:22:16 +01001024 SetDrawingMode(DM_DIRECTX);
1025
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001026 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
1027 FLOAT(w), FLOAT(h), &textLayout);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001028
1029 if (SUCCEEDED(hr))
1030 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001031 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
1032 textLayout->SetFontWeight(mFontWeight, textRange);
1033 textLayout->SetFontStyle(mFontStyle, textRange);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001034
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001035 TextRenderer renderer(this);
1036 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
Bram Moolenaar60ebd522019-03-21 20:50:12 +01001037 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001038 }
1039
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001040 SafeRelease(&textLayout);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001041}
1042
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001043 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001044DWriteContext::FillRect(const RECT *rc, COLORREF color)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001045{
Bram Moolenaar92467d32017-12-05 13:22:16 +01001046 if (mDMode == DM_INTEROP)
1047 {
1048 // GDI functions are used before this call. Keep using GDI.
1049 // (Switching to Direct2D causes terrible slowdown.)
1050 HBRUSH hbr = ::CreateSolidBrush(color);
1051 ::FillRect(mInteropHDC, rc, hbr);
1052 ::DeleteObject(HGDIOBJ(hbr));
1053 }
1054 else
1055 {
1056 SetDrawingMode(DM_DIRECTX);
1057 mRT->FillRectangle(
1058 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top),
1059 FLOAT(rc->right), FLOAT(rc->bottom)),
1060 SolidBrush(color));
1061 }
1062}
1063
1064 void
1065DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color)
1066{
1067 if (mDMode == DM_INTEROP)
1068 {
1069 // GDI functions are used before this call. Keep using GDI.
1070 // (Switching to Direct2D causes terrible slowdown.)
1071 HPEN hpen = ::CreatePen(PS_SOLID, 1, color);
1072 HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen));
1073 ::MoveToEx(mInteropHDC, x1, y1, NULL);
1074 ::LineTo(mInteropHDC, x2, y2);
1075 ::SelectObject(mInteropHDC, old_pen);
1076 ::DeleteObject(HGDIOBJ(hpen));
1077 }
1078 else
1079 {
1080 SetDrawingMode(DM_DIRECTX);
1081 mRT->DrawLine(
1082 D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f),
1083 D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f),
1084 SolidBrush(color));
1085 }
1086}
1087
1088 void
1089DWriteContext::SetPixel(int x, int y, COLORREF color)
1090{
1091 if (mDMode == DM_INTEROP)
1092 {
1093 // GDI functions are used before this call. Keep using GDI.
1094 // (Switching to Direct2D causes terrible slowdown.)
1095 ::SetPixel(mInteropHDC, x, y, color);
1096 }
1097 else
1098 {
1099 SetDrawingMode(DM_DIRECTX);
1100 // Direct2D doesn't have SetPixel API. Use DrawLine instead.
1101 mRT->DrawLine(
1102 D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f),
1103 D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f),
1104 SolidBrush(color));
1105 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001106}
1107
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001108 void
Bram Moolenaara338adc2018-01-31 20:51:47 +01001109DWriteContext::Scroll(int x, int y, const RECT *rc)
1110{
1111 SetDrawingMode(DM_DIRECTX);
1112 mRT->Flush();
1113
1114 D2D1_RECT_U srcRect;
1115 D2D1_POINT_2U destPoint;
1116 if (x >= 0)
1117 {
1118 srcRect.left = rc->left;
1119 srcRect.right = rc->right - x;
1120 destPoint.x = rc->left + x;
1121 }
1122 else
1123 {
1124 srcRect.left = rc->left - x;
1125 srcRect.right = rc->right;
1126 destPoint.x = rc->left;
1127 }
1128 if (y >= 0)
1129 {
1130 srcRect.top = rc->top;
1131 srcRect.bottom = rc->bottom - y;
1132 destPoint.y = rc->top + y;
1133 }
1134 else
1135 {
1136 srcRect.top = rc->top - y;
1137 srcRect.bottom = rc->bottom;
1138 destPoint.y = rc->top;
1139 }
1140 mBitmap->CopyFromRenderTarget(&destPoint, mRT, &srcRect);
1141
1142 D2D1_RECT_F destRect = {
1143 FLOAT(destPoint.x), FLOAT(destPoint.y),
1144 FLOAT(destPoint.x + srcRect.right - srcRect.left),
1145 FLOAT(destPoint.y + srcRect.bottom - srcRect.top)
1146 };
1147 mRT->DrawBitmap(mBitmap, destRect, 1.0F,
1148 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, destRect);
1149}
1150
1151 void
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001152DWriteContext::Flush()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001153{
Bram Moolenaar92467d32017-12-05 13:22:16 +01001154 SetDrawingMode(DM_GDI);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001155}
1156
1157 void
1158DWriteContext::SetRenderingParams(
1159 const DWriteRenderingParams *params)
1160{
1161 if (mDWriteFactory == NULL)
1162 return;
1163
1164 IDWriteRenderingParams *renderingParams = NULL;
1165 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =
1166 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
1167 HRESULT hr;
1168 if (params != NULL)
1169 {
1170 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,
1171 params->enhancedContrast, params->clearTypeLevel,
1172 ToPixelGeometry(params->pixelGeometry),
1173 ToRenderingMode(params->renderingMode), &renderingParams);
1174 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);
1175 }
1176 else
1177 hr = mDWriteFactory->CreateRenderingParams(&renderingParams);
1178 if (SUCCEEDED(hr) && renderingParams != NULL)
1179 {
1180 SafeRelease(&mRenderingParams);
1181 mRenderingParams = renderingParams;
1182 mTextAntialiasMode = textAntialiasMode;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001183
1184 Flush();
1185 mRT->SetTextRenderingParams(mRenderingParams);
1186 mRT->SetTextAntialiasMode(mTextAntialiasMode);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001187 }
1188}
1189
1190 DWriteRenderingParams *
1191DWriteContext::GetRenderingParams(
1192 DWriteRenderingParams *params)
1193{
1194 if (params != NULL && mRenderingParams != NULL)
1195 {
1196 params->gamma = mRenderingParams->GetGamma();
1197 params->enhancedContrast = mRenderingParams->GetEnhancedContrast();
1198 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();
1199 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());
1200 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());
1201 params->textAntialiasMode = mTextAntialiasMode;
1202 }
1203 return params;
1204}
1205
1206////////////////////////////////////////////////////////////////////////////
1207// PUBLIC C INTERFACES
1208
1209 void
1210DWrite_Init(void)
1211{
1212#ifdef DYNAMIC_DIRECTX
1213 // Load libraries.
1214 hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll"));
1215 hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll"));
1216 if (hD2D1DLL == NULL || hDWriteDLL == NULL)
1217 {
1218 DWrite_Final();
1219 return;
1220 }
1221 // Get address of procedures.
1222 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(
1223 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");
1224 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,
1225 "D2D1CreateFactory");
1226 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,
1227 "DWriteCreateFactory");
1228#endif
1229}
1230
1231 void
1232DWrite_Final(void)
1233{
1234#ifdef DYNAMIC_DIRECTX
1235 pGetUserDefaultLocaleName = NULL;
1236 pD2D1CreateFactory = NULL;
1237 pDWriteCreateFactory = NULL;
1238 unload(hDWriteDLL);
1239 unload(hD2D1DLL);
1240#endif
1241}
1242
1243 DWriteContext *
1244DWriteContext_Open(void)
1245{
1246#ifdef DYNAMIC_DIRECTX
1247 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL
1248 || pDWriteCreateFactory == NULL)
1249 return NULL;
1250#endif
1251 return new DWriteContext();
1252}
1253
1254 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001255DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001256{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001257 if (ctx != NULL)
1258 ctx->BindDC(hdc, rect);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001259}
1260
1261 void
1262DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
1263{
1264 if (ctx != NULL)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001265 ctx->SetFont(hFont);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001266}
1267
1268 void
1269DWriteContext_DrawText(
1270 DWriteContext *ctx,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001271 const WCHAR *text,
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001272 int len,
1273 int x,
1274 int y,
1275 int w,
1276 int h,
1277 int cellWidth,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001278 COLORREF color,
1279 UINT fuOptions,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001280 const RECT *lprc,
1281 const INT *lpDx)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001282{
1283 if (ctx != NULL)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001284 ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
1285 fuOptions, lprc, lpDx);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001286}
1287
1288 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001289DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001290{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001291 if (ctx != NULL)
1292 ctx->FillRect(rc, color);
1293}
1294
1295 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001296DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2,
1297 COLORREF color)
1298{
1299 if (ctx != NULL)
1300 ctx->DrawLine(x1, y1, x2, y2, color);
1301}
1302
1303 void
1304DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color)
1305{
1306 if (ctx != NULL)
1307 ctx->SetPixel(x, y, color);
1308}
1309
1310 void
Bram Moolenaara338adc2018-01-31 20:51:47 +01001311DWriteContext_Scroll(DWriteContext *ctx, int x, int y, const RECT *rc)
1312{
1313 if (ctx != NULL)
1314 ctx->Scroll(x, y, rc);
1315}
1316
1317 void
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001318DWriteContext_Flush(DWriteContext *ctx)
1319{
1320 if (ctx != NULL)
1321 ctx->Flush();
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001322}
1323
1324 void
1325DWriteContext_Close(DWriteContext *ctx)
1326{
1327 delete ctx;
1328}
1329
1330 void
1331DWriteContext_SetRenderingParams(
1332 DWriteContext *ctx,
1333 const DWriteRenderingParams *params)
1334{
1335 if (ctx != NULL)
1336 ctx->SetRenderingParams(params);
1337}
1338
1339 DWriteRenderingParams *
1340DWriteContext_GetRenderingParams(
1341 DWriteContext *ctx,
1342 DWriteRenderingParams *params)
1343{
1344 if (ctx != NULL)
1345 return ctx->GetRenderingParams(params);
1346 else
1347 return NULL;
1348}