blob: 4f24390775ed623f51621051762027eebd1056c0 [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 Moolenaar35d7a2f2022-06-09 20:53:54 +010055#ifdef __MINGW32__
56# define UNUSED __attribute__((unused))
57#else
58# define UNUSED
59#endif
60
Bram Moolenaarcc6cf9b2016-03-19 20:51:35 +010061#if (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L)
62# define FINAL final
63#else
64# define FINAL
65#endif
66
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020067#ifdef DYNAMIC_DIRECTX
K.Takatad68b2fc2022-02-12 11:18:37 +000068extern "C" HINSTANCE vimLoadLib(const char *name);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020069
70typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int);
71typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE,
72 REFIID, const D2D1_FACTORY_OPTIONS *, void **);
73typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE,
74 REFIID, IUnknown **);
75
76static HINSTANCE hD2D1DLL = NULL;
77static HINSTANCE hDWriteDLL = NULL;
78
79static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL;
80static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL;
81static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL;
82
83#define GetUserDefaultLocaleName (*pGetUserDefaultLocaleName)
84#define D2D1CreateFactory (*pD2D1CreateFactory)
85#define DWriteCreateFactory (*pDWriteCreateFactory)
86
87 static void
88unload(HINSTANCE &hinst)
89{
90 if (hinst != NULL)
91 {
92 FreeLibrary(hinst);
93 hinst = NULL;
94 }
95}
96#endif // DYNAMIC_DIRECTX
97
98template <class T> inline void SafeRelease(T **ppT)
99{
100 if (*ppT)
101 {
102 (*ppT)->Release();
103 *ppT = NULL;
104 }
105}
106
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200107 static DWRITE_PIXEL_GEOMETRY
108ToPixelGeometry(int value)
109{
110 switch (value)
111 {
112 default:
113 case 0:
114 return DWRITE_PIXEL_GEOMETRY_FLAT;
115 case 1:
116 return DWRITE_PIXEL_GEOMETRY_RGB;
117 case 2:
118 return DWRITE_PIXEL_GEOMETRY_BGR;
119 }
120}
121
122 static int
123ToInt(DWRITE_PIXEL_GEOMETRY value)
124{
125 switch (value)
126 {
127 case DWRITE_PIXEL_GEOMETRY_FLAT:
128 return 0;
129 case DWRITE_PIXEL_GEOMETRY_RGB:
130 return 1;
131 case DWRITE_PIXEL_GEOMETRY_BGR:
132 return 2;
133 default:
134 return -1;
135 }
136}
137
138 static DWRITE_RENDERING_MODE
139ToRenderingMode(int value)
140{
141 switch (value)
142 {
143 default:
144 case 0:
145 return DWRITE_RENDERING_MODE_DEFAULT;
146 case 1:
147 return DWRITE_RENDERING_MODE_ALIASED;
148 case 2:
149 return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
150 case 3:
151 return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL;
152 case 4:
153 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
154 case 5:
155 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
156 case 6:
157 return DWRITE_RENDERING_MODE_OUTLINE;
158 }
159}
160
161 static D2D1_TEXT_ANTIALIAS_MODE
162ToTextAntialiasMode(int value)
163{
164 switch (value)
165 {
166 default:
167 case 0:
168 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
169 case 1:
170 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
171 case 2:
172 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
173 case 3:
174 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
175 }
176}
177
178 static int
179ToInt(DWRITE_RENDERING_MODE value)
180{
181 switch (value)
182 {
183 case DWRITE_RENDERING_MODE_DEFAULT:
184 return 0;
185 case DWRITE_RENDERING_MODE_ALIASED:
186 return 1;
187 case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
188 return 2;
189 case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
190 return 3;
191 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
192 return 4;
193 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
194 return 5;
195 case DWRITE_RENDERING_MODE_OUTLINE:
196 return 6;
197 default:
198 return -1;
199 }
200}
201
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100202class FontCache {
203public:
204 struct Item {
205 HFONT hFont;
206 IDWriteTextFormat* pTextFormat;
207 DWRITE_FONT_WEIGHT fontWeight;
208 DWRITE_FONT_STYLE fontStyle;
209 Item() : hFont(NULL), pTextFormat(NULL) {}
210 };
211
212private:
213 int mSize;
214 Item *mItems;
215
216public:
217 FontCache(int size = 2) :
218 mSize(size),
219 mItems(new Item[size])
220 {
221 }
222
223 ~FontCache()
224 {
225 for (int i = 0; i < mSize; ++i)
226 SafeRelease(&mItems[i].pTextFormat);
227 delete[] mItems;
228 }
229
230 bool get(HFONT hFont, Item &item)
231 {
232 int n = find(hFont);
233 if (n < 0)
234 return false;
235 item = mItems[n];
236 slide(n);
237 return true;
238 }
239
240 void put(const Item& item)
241 {
242 int n = find(item.hFont);
243 if (n < 0)
244 n = mSize - 1;
245 if (mItems[n].pTextFormat != item.pTextFormat)
246 {
247 SafeRelease(&mItems[n].pTextFormat);
Bram Moolenaar1b3e0722020-12-08 21:12:40 +0100248 if (item.pTextFormat != NULL)
249 item.pTextFormat->AddRef();
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100250 }
251 mItems[n] = item;
252 slide(n);
253 }
254
255private:
256 int find(HFONT hFont)
257 {
258 for (int i = 0; i < mSize; ++i)
259 {
260 if (mItems[i].hFont == hFont)
261 return i;
262 }
263 return -1;
264 }
265
266 void slide(int nextTop)
267 {
268 if (nextTop == 0)
269 return;
270 Item tmp = mItems[nextTop];
271 for (int i = nextTop - 1; i >= 0; --i)
272 mItems[i + 1] = mItems[i];
273 mItems[0] = tmp;
274 }
275};
276
Bram Moolenaar92467d32017-12-05 13:22:16 +0100277enum DrawingMode {
278 DM_GDI = 0,
279 DM_DIRECTX = 1,
280 DM_INTEROP = 2,
281};
282
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100283struct DWriteContext {
284 HDC mHDC;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100285 RECT mBindRect;
286 DrawingMode mDMode;
287 HDC mInteropHDC;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100288 bool mDrawing;
289 bool mFallbackDC;
290
291 ID2D1Factory *mD2D1Factory;
292
293 ID2D1DCRenderTarget *mRT;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100294 ID2D1GdiInteropRenderTarget *mGDIRT;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100295 ID2D1SolidColorBrush *mBrush;
Bram Moolenaara338adc2018-01-31 20:51:47 +0100296 ID2D1Bitmap *mBitmap;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100297
298 IDWriteFactory *mDWriteFactory;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100299#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100300 IDWriteFactory2 *mDWriteFactory2;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100301#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100302
303 IDWriteGdiInterop *mGdiInterop;
304 IDWriteRenderingParams *mRenderingParams;
305
306 FontCache mFontCache;
307 IDWriteTextFormat *mTextFormat;
308 DWRITE_FONT_WEIGHT mFontWeight;
309 DWRITE_FONT_STYLE mFontStyle;
310
311 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
312
313 // METHODS
314
315 DWriteContext();
316
317 virtual ~DWriteContext();
318
Bram Moolenaar92467d32017-12-05 13:22:16 +0100319 HRESULT CreateDeviceResources();
320
321 void DiscardDeviceResources();
322
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100323 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
324 IDWriteTextFormat **ppTextFormat);
325
326 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
327
328 void SetFont(HFONT hFont);
329
Bram Moolenaara338adc2018-01-31 20:51:47 +0100330 void Rebind();
331
Bram Moolenaar92467d32017-12-05 13:22:16 +0100332 void BindDC(HDC hdc, const RECT *rect);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100333
Bram Moolenaar92467d32017-12-05 13:22:16 +0100334 HRESULT SetDrawingMode(DrawingMode mode);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100335
336 ID2D1Brush* SolidBrush(COLORREF color);
337
Bram Moolenaar92467d32017-12-05 13:22:16 +0100338 void DrawText(const WCHAR *text, int len,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100339 int x, int y, int w, int h, int cellWidth, COLORREF color,
Bram Moolenaar92467d32017-12-05 13:22:16 +0100340 UINT fuOptions, const RECT *lprc, const INT *lpDx);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100341
Bram Moolenaar92467d32017-12-05 13:22:16 +0100342 void FillRect(const RECT *rc, COLORREF color);
343
344 void DrawLine(int x1, int y1, int x2, int y2, COLORREF color);
345
346 void SetPixel(int x, int y, COLORREF color);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100347
Bram Moolenaara338adc2018-01-31 20:51:47 +0100348 void Scroll(int x, int y, const RECT *rc);
349
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100350 void Flush();
351
352 void SetRenderingParams(
353 const DWriteRenderingParams *params);
354
355 DWriteRenderingParams *GetRenderingParams(
356 DWriteRenderingParams *params);
357};
358
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200359class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
360{
361private:
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100362 FLOAT &mAccum;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200363 FLOAT mDelta;
364 FLOAT *mAdjustedAdvances;
365
366public:
367 AdjustedGlyphRun(
368 const DWRITE_GLYPH_RUN *glyphRun,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100369 FLOAT cellWidth,
370 FLOAT &accum) :
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200371 DWRITE_GLYPH_RUN(*glyphRun),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100372 mAccum(accum),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200373 mDelta(0.0f),
374 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
375 {
376 assert(cellWidth != 0.0f);
377 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
378 {
379 FLOAT orig = glyphRun->glyphAdvances[i];
380 FLOAT adjusted = adjustToCell(orig, cellWidth);
381 mAdjustedAdvances[i] = adjusted;
382 mDelta += adjusted - orig;
383 }
384 glyphAdvances = mAdjustedAdvances;
385 }
386
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100387 ~AdjustedGlyphRun()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200388 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100389 mAccum += mDelta;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200390 delete[] mAdjustedAdvances;
391 }
392
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200393 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
394 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100395 int cellCount = int(floor(value / cellWidth + 0.5f));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200396 if (cellCount < 1)
397 cellCount = 1;
398 return cellCount * cellWidth;
399 }
400};
401
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100402struct TextRendererContext {
403 // const fields.
404 COLORREF color;
405 FLOAT cellWidth;
406
407 // working fields.
408 FLOAT offsetX;
409};
410
411class TextRenderer FINAL : public IDWriteTextRenderer
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200412{
413public:
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100414 TextRenderer(
415 DWriteContext* pDWC) :
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200416 cRefCount_(0),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100417 pDWC_(pDWC)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200418 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200419 AddRef();
420 }
421
Bram Moolenaaredb4f2b2016-02-27 15:27:23 +0100422 // add "virtual" to avoid a compiler warning
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100423 virtual ~TextRenderer()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200424 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200425 }
426
427 IFACEMETHOD(IsPixelSnappingDisabled)(
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100428 __maybenull void* clientDrawingContext UNUSED,
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200429 __out BOOL* isDisabled)
430 {
431 *isDisabled = FALSE;
432 return S_OK;
433 }
434
435 IFACEMETHOD(GetCurrentTransform)(
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100436 __maybenull void* clientDrawingContext UNUSED,
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200437 __out DWRITE_MATRIX* transform)
438 {
Bram Moolenaaredb4f2b2016-02-27 15:27:23 +0100439 // forward the render target's transform
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100440 pDWC_->mRT->GetTransform(
441 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200442 return S_OK;
443 }
444
445 IFACEMETHOD(GetPixelsPerDip)(
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100446 __maybenull void* clientDrawingContext UNUSED,
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200447 __out FLOAT* pixelsPerDip)
448 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100449 float dpiX, unused;
450 pDWC_->mRT->GetDpi(&dpiX, &unused);
451 *pixelsPerDip = dpiX / 96.0f;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200452 return S_OK;
453 }
454
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200455 IFACEMETHOD(DrawUnderline)(
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100456 __maybenull void* clientDrawingContext UNUSED,
457 FLOAT baselineOriginX UNUSED,
458 FLOAT baselineOriginY UNUSED,
459 __in DWRITE_UNDERLINE const* underline UNUSED,
460 IUnknown* clientDrawingEffect UNUSED)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200461 {
462 return E_NOTIMPL;
463 }
464
465 IFACEMETHOD(DrawStrikethrough)(
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100466 __maybenull void* clientDrawingContext UNUSED,
467 FLOAT baselineOriginX UNUSED,
468 FLOAT baselineOriginY UNUSED,
469 __in DWRITE_STRIKETHROUGH const* strikethrough UNUSED,
470 IUnknown* clientDrawingEffect UNUSED)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200471 {
472 return E_NOTIMPL;
473 }
474
475 IFACEMETHOD(DrawInlineObject)(
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100476 __maybenull void* clientDrawingContext UNUSED,
477 FLOAT originX UNUSED,
478 FLOAT originY UNUSED,
479 IDWriteInlineObject* inlineObject UNUSED,
480 BOOL isSideways UNUSED,
481 BOOL isRightToLeft UNUSED,
482 IUnknown* clientDrawingEffect UNUSED)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200483 {
484 return E_NOTIMPL;
485 }
486
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100487 IFACEMETHOD(DrawGlyphRun)(
488 __maybenull void* clientDrawingContext,
489 FLOAT baselineOriginX,
490 FLOAT baselineOriginY,
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100491 DWRITE_MEASURING_MODE measuringMode UNUSED,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100492 __in DWRITE_GLYPH_RUN const* glyphRun,
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100493 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription UNUSED,
494 IUnknown* clientDrawingEffect UNUSED)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100495 {
496 TextRendererContext *context =
497 reinterpret_cast<TextRendererContext*>(clientDrawingContext);
498
499 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth,
500 context->offsetX);
501
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100502#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100503 if (pDWC_->mDWriteFactory2 != NULL)
504 {
505 IDWriteColorGlyphRunEnumerator *enumerator = NULL;
506 HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun(
507 baselineOriginX + context->offsetX,
508 baselineOriginY,
509 &adjustedGlyphRun,
510 NULL,
511 DWRITE_MEASURING_MODE_GDI_NATURAL,
512 NULL,
513 0,
514 &enumerator);
515 if (SUCCEEDED(hr))
516 {
517 // Draw by IDWriteFactory2 for color emoji
518 BOOL hasRun = TRUE;
519 enumerator->MoveNext(&hasRun);
520 while (hasRun)
521 {
522 const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun;
523 enumerator->GetCurrentRun(&colorGlyphRun);
524
525 pDWC_->mBrush->SetColor(colorGlyphRun->runColor);
526 pDWC_->mRT->DrawGlyphRun(
527 D2D1::Point2F(
528 colorGlyphRun->baselineOriginX,
529 colorGlyphRun->baselineOriginY),
530 &colorGlyphRun->glyphRun,
531 pDWC_->mBrush,
532 DWRITE_MEASURING_MODE_NATURAL);
533 enumerator->MoveNext(&hasRun);
534 }
535 SafeRelease(&enumerator);
536 return S_OK;
537 }
538 }
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100539#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100540
541 // Draw by IDWriteFactory (without color emoji)
542 pDWC_->mRT->DrawGlyphRun(
543 D2D1::Point2F(
544 baselineOriginX + context->offsetX,
545 baselineOriginY),
546 &adjustedGlyphRun,
547 pDWC_->SolidBrush(context->color),
548 DWRITE_MEASURING_MODE_NATURAL);
549 return S_OK;
550 }
551
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200552public:
553 IFACEMETHOD_(unsigned long, AddRef) ()
554 {
555 return InterlockedIncrement(&cRefCount_);
556 }
557
558 IFACEMETHOD_(unsigned long, Release) ()
559 {
560 long newCount = InterlockedDecrement(&cRefCount_);
561
562 if (newCount == 0)
563 {
564 delete this;
565 return 0;
566 }
567 return newCount;
568 }
569
570 IFACEMETHOD(QueryInterface)(
571 IID const& riid,
572 void** ppvObject)
573 {
574 if (__uuidof(IDWriteTextRenderer) == riid)
575 {
576 *ppvObject = this;
577 }
578 else if (__uuidof(IDWritePixelSnapping) == riid)
579 {
580 *ppvObject = this;
581 }
582 else if (__uuidof(IUnknown) == riid)
583 {
584 *ppvObject = this;
585 }
586 else
587 {
588 *ppvObject = NULL;
589 return E_FAIL;
590 }
591
592 return S_OK;
593 }
594
595private:
Bram Moolenaar0106b4b2014-08-07 13:55:10 +0200596 long cRefCount_;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100597 DWriteContext* pDWC_;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200598};
599
600DWriteContext::DWriteContext() :
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100601 mHDC(NULL),
Bram Moolenaar92467d32017-12-05 13:22:16 +0100602 mBindRect(),
603 mDMode(DM_GDI),
604 mInteropHDC(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200605 mDrawing(false),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100606 mFallbackDC(false),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200607 mD2D1Factory(NULL),
608 mRT(NULL),
Bram Moolenaar92467d32017-12-05 13:22:16 +0100609 mGDIRT(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200610 mBrush(NULL),
Bram Moolenaara338adc2018-01-31 20:51:47 +0100611 mBitmap(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200612 mDWriteFactory(NULL),
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100613#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100614 mDWriteFactory2(NULL),
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100615#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200616 mGdiInterop(NULL),
617 mRenderingParams(NULL),
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100618 mFontCache(8),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200619 mTextFormat(NULL),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200620 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
621 mFontStyle(DWRITE_FONT_STYLE_NORMAL),
622 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
623{
624 HRESULT hr;
625
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200626 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
627 __uuidof(ID2D1Factory), NULL,
628 reinterpret_cast<void**>(&mD2D1Factory));
629 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
630
631 if (SUCCEEDED(hr))
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200632 {
633 hr = DWriteCreateFactory(
634 DWRITE_FACTORY_TYPE_SHARED,
635 __uuidof(IDWriteFactory),
636 reinterpret_cast<IUnknown**>(&mDWriteFactory));
637 _RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,
638 mDWriteFactory);
639 }
640
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100641#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200642 if (SUCCEEDED(hr))
643 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100644 DWriteCreateFactory(
645 DWRITE_FACTORY_TYPE_SHARED,
646 __uuidof(IDWriteFactory2),
647 reinterpret_cast<IUnknown**>(&mDWriteFactory2));
648 _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available");
649 }
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100650#endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100651
652 if (SUCCEEDED(hr))
653 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200654 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
655 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
656 }
657
658 if (SUCCEEDED(hr))
659 {
660 hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);
661 _RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,
662 mRenderingParams);
663 }
664}
665
666DWriteContext::~DWriteContext()
667{
668 SafeRelease(&mTextFormat);
669 SafeRelease(&mRenderingParams);
670 SafeRelease(&mGdiInterop);
671 SafeRelease(&mDWriteFactory);
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100672#ifdef FEAT_DIRECTX_COLOR_EMOJI
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100673 SafeRelease(&mDWriteFactory2);
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100674#endif
Bram Moolenaara338adc2018-01-31 20:51:47 +0100675 SafeRelease(&mBitmap);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200676 SafeRelease(&mBrush);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100677 SafeRelease(&mGDIRT);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200678 SafeRelease(&mRT);
679 SafeRelease(&mD2D1Factory);
680}
681
682 HRESULT
Bram Moolenaar92467d32017-12-05 13:22:16 +0100683DWriteContext::CreateDeviceResources()
684{
685 HRESULT hr;
686
687 if (mRT != NULL)
688 return S_OK;
689
690 D2D1_RENDER_TARGET_PROPERTIES props = {
691 D2D1_RENDER_TARGET_TYPE_DEFAULT,
692 { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },
693 0, 0,
694 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
695 D2D1_FEATURE_LEVEL_DEFAULT
696 };
697 hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);
698 _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);
699
700 if (SUCCEEDED(hr))
701 {
702 // This always succeeds.
703 mRT->QueryInterface(
704 __uuidof(ID2D1GdiInteropRenderTarget),
705 reinterpret_cast<void**>(&mGDIRT));
706 _RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT);
707 }
708
709 if (SUCCEEDED(hr))
710 {
711 hr = mRT->CreateSolidColorBrush(
712 D2D1::ColorF(D2D1::ColorF::Black),
713 &mBrush);
714 _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);
715 }
716
717 if (SUCCEEDED(hr))
Bram Moolenaara338adc2018-01-31 20:51:47 +0100718 Rebind();
Bram Moolenaar92467d32017-12-05 13:22:16 +0100719
720 return hr;
721}
722
723 void
724DWriteContext::DiscardDeviceResources()
725{
Bram Moolenaara338adc2018-01-31 20:51:47 +0100726 SafeRelease(&mBitmap);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100727 SafeRelease(&mBrush);
728 SafeRelease(&mGDIRT);
729 SafeRelease(&mRT);
730}
731
732 HRESULT
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100733DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
734 IDWriteTextFormat **ppTextFormat)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200735{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100736 // 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 +0200737 HRESULT hr = S_OK;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100738 IDWriteTextFormat *pTextFormat = NULL;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200739
740 IDWriteFont *font = NULL;
741 IDWriteFontFamily *fontFamily = NULL;
742 IDWriteLocalizedStrings *localizedFamilyNames = NULL;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100743 float fontSize = 0;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200744
745 if (SUCCEEDED(hr))
746 {
747 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
748 }
749
750 // Get the font family to which this font belongs.
751 if (SUCCEEDED(hr))
752 {
753 hr = font->GetFontFamily(&fontFamily);
754 }
755
756 // Get the family names. This returns an object that encapsulates one or
757 // more names with the same meaning but in different languages.
758 if (SUCCEEDED(hr))
759 {
760 hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
761 }
762
763 // Get the family name at index zero. If we were going to display the name
764 // we'd want to try to find one that matched the use locale, but for
765 // purposes of creating a text format object any language will do.
766
767 wchar_t familyName[100];
768 if (SUCCEEDED(hr))
769 {
770 hr = localizedFamilyNames->GetString(0, familyName,
771 ARRAYSIZE(familyName));
772 }
773
774 if (SUCCEEDED(hr))
775 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100776 // Use lfHeight of the LOGFONT as font size.
777 fontSize = float(logFont.lfHeight);
778
779 if (fontSize < 0)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200780 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100781 // Negative lfHeight represents the size of the em unit.
782 fontSize = -fontSize;
783 }
784 else
785 {
786 // Positive lfHeight represents the cell height (ascent +
787 // descent).
788 DWRITE_FONT_METRICS fontMetrics;
789 font->GetMetrics(&fontMetrics);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200790
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100791 // Convert the cell height (ascent + descent) from design units
792 // to ems.
793 float cellHeight = static_cast<float>(
794 fontMetrics.ascent + fontMetrics.descent)
795 / fontMetrics.designUnitsPerEm;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200796
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100797 // Divide the font size by the cell height to get the font em
798 // size.
799 fontSize /= cellHeight;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200800 }
801 }
802
803 // The text format includes a locale name. Ideally, this would be the
804 // language of the text, which may or may not be the same as the primary
805 // language of the user. However, for our purposes the user locale will do.
806 wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
807 if (SUCCEEDED(hr))
808 {
809 if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
810 hr = HRESULT_FROM_WIN32(GetLastError());
811 }
812
813 if (SUCCEEDED(hr))
814 {
815 // Create the text format object.
816 hr = mDWriteFactory->CreateTextFormat(
817 familyName,
818 NULL, // no custom font collection
819 font->GetWeight(),
820 font->GetStyle(),
821 font->GetStretch(),
822 fontSize,
823 localeName,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100824 &pTextFormat);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200825 }
826
827 if (SUCCEEDED(hr))
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100828 hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
829
830 if (SUCCEEDED(hr))
831 hr = pTextFormat->SetParagraphAlignment(
Bram Moolenaar1f271ef2019-03-16 15:24:42 +0100832 DWRITE_PARAGRAPH_ALIGNMENT_FAR);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100833
834 if (SUCCEEDED(hr))
835 hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200836
837 SafeRelease(&localizedFamilyNames);
838 SafeRelease(&fontFamily);
839 SafeRelease(&font);
840
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100841 if (SUCCEEDED(hr))
842 *ppTextFormat = pTextFormat;
843 else
844 SafeRelease(&pTextFormat);
845
846 return hr;
847}
848
849 HRESULT
850DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont)
851{
852 HRESULT hr = S_OK;
853 IDWriteTextFormat *pTextFormat = NULL;
854
855 hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat);
856
857 if (SUCCEEDED(hr))
858 {
859 SafeRelease(&mTextFormat);
860 mTextFormat = pTextFormat;
861 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
862 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
863 : DWRITE_FONT_STYLE_NORMAL;
864 }
865
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200866 return hr;
867}
868
869 void
870DWriteContext::SetFont(HFONT hFont)
871{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100872 FontCache::Item item;
873 if (mFontCache.get(hFont, item))
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200874 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100875 if (item.pTextFormat != NULL)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200876 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100877 item.pTextFormat->AddRef();
878 SafeRelease(&mTextFormat);
879 mTextFormat = item.pTextFormat;
880 mFontWeight = item.fontWeight;
881 mFontStyle = item.fontStyle;
882 mFallbackDC = false;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200883 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100884 else
885 mFallbackDC = true;
886 return;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200887 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100888
889 HRESULT hr = E_FAIL;
890 LOGFONTW lf;
891 if (GetObjectW(hFont, sizeof(lf), &lf))
892 hr = SetFontByLOGFONT(lf);
893
894 item.hFont = hFont;
895 if (SUCCEEDED(hr))
896 {
897 item.pTextFormat = mTextFormat;
898 item.fontWeight = mFontWeight;
899 item.fontStyle = mFontStyle;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100900 mFallbackDC = false;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100901 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100902 else
903 mFallbackDC = true;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100904 mFontCache.put(item);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200905}
906
907 void
Bram Moolenaara338adc2018-01-31 20:51:47 +0100908DWriteContext::Rebind()
909{
910 SafeRelease(&mBitmap);
911
912 mRT->BindDC(mHDC, &mBindRect);
913 mRT->SetTransform(D2D1::IdentityMatrix());
914
915 D2D1_BITMAP_PROPERTIES props = {
916 {DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE},
917 96.0f, 96.0f
918 };
919 mRT->CreateBitmap(
920 D2D1::SizeU(mBindRect.right - mBindRect.left,
921 mBindRect.bottom - mBindRect.top),
922 props, &mBitmap);
923}
924
925 void
Bram Moolenaar92467d32017-12-05 13:22:16 +0100926DWriteContext::BindDC(HDC hdc, const RECT *rect)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200927{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100928 mHDC = hdc;
Bram Moolenaar92467d32017-12-05 13:22:16 +0100929 mBindRect = *rect;
Bram Moolenaara338adc2018-01-31 20:51:47 +0100930
931 if (mRT == NULL)
932 CreateDeviceResources();
933 else
934 {
935 Flush();
936 Rebind();
937 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200938}
939
Bram Moolenaar3dd174a2019-03-25 22:48:18 +0100940extern "C" void redraw_later_clear(void);
941
Bram Moolenaar92467d32017-12-05 13:22:16 +0100942 HRESULT
943DWriteContext::SetDrawingMode(DrawingMode mode)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200944{
Bram Moolenaar92467d32017-12-05 13:22:16 +0100945 HRESULT hr = S_OK;
946
947 switch (mode)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100948 {
Bram Moolenaar92467d32017-12-05 13:22:16 +0100949 default:
950 case DM_GDI:
951 if (mInteropHDC != NULL)
952 {
953 mGDIRT->ReleaseDC(NULL);
954 mInteropHDC = NULL;
955 }
956 if (mDrawing)
957 {
958 hr = mRT->EndDraw();
Bram Moolenaarf653a6b2019-05-05 13:20:02 +0200959 if (hr == (HRESULT)D2DERR_RECREATE_TARGET)
Bram Moolenaar92467d32017-12-05 13:22:16 +0100960 {
961 hr = S_OK;
962 DiscardDeviceResources();
963 CreateDeviceResources();
Bram Moolenaar3dd174a2019-03-25 22:48:18 +0100964 redraw_later_clear();
Bram Moolenaar92467d32017-12-05 13:22:16 +0100965 }
966 mDrawing = false;
967 }
968 break;
969
970 case DM_DIRECTX:
971 if (mInteropHDC != NULL)
972 {
973 mGDIRT->ReleaseDC(NULL);
974 mInteropHDC = NULL;
975 }
976 else if (mDrawing == false)
977 {
978 CreateDeviceResources();
979 mRT->BeginDraw();
980 mDrawing = true;
981 }
982 break;
983
984 case DM_INTEROP:
985 if (mDrawing == false)
986 {
987 CreateDeviceResources();
988 mRT->BeginDraw();
989 mDrawing = true;
990 }
991 if (mInteropHDC == NULL)
992 hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC);
993 break;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100994 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100995 mDMode = mode;
996 return hr;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100997}
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200998
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100999 ID2D1Brush*
1000DWriteContext::SolidBrush(COLORREF color)
1001{
1002 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 |
1003 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color))));
1004 return mBrush;
1005}
1006
1007 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001008DWriteContext::DrawText(const WCHAR *text, int len,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001009 int x, int y, int w, int h, int cellWidth, COLORREF color,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001010 UINT fuOptions, const RECT *lprc, const INT *lpDx)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001011{
1012 if (mFallbackDC)
1013 {
Bram Moolenaar92467d32017-12-05 13:22:16 +01001014 // Fall back to GDI rendering.
1015 HRESULT hr = SetDrawingMode(DM_INTEROP);
1016 if (SUCCEEDED(hr))
1017 {
1018 HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT);
1019 HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont);
1020 ::SetTextColor(mInteropHDC, color);
1021 ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC));
1022 ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx);
1023 ::SelectObject(mInteropHDC, hOldFont);
1024 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001025 return;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001026 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001027
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001028 HRESULT hr;
1029 IDWriteTextLayout *textLayout = NULL;
1030
Bram Moolenaar92467d32017-12-05 13:22:16 +01001031 SetDrawingMode(DM_DIRECTX);
1032
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001033 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
1034 FLOAT(w), FLOAT(h), &textLayout);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001035
1036 if (SUCCEEDED(hr))
1037 {
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001038 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
1039 textLayout->SetFontWeight(mFontWeight, textRange);
1040 textLayout->SetFontStyle(mFontStyle, textRange);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001041
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001042 TextRenderer renderer(this);
1043 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
Bram Moolenaar60ebd522019-03-21 20:50:12 +01001044 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y));
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001045 }
1046
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001047 SafeRelease(&textLayout);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001048}
1049
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001050 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001051DWriteContext::FillRect(const RECT *rc, COLORREF color)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001052{
Bram Moolenaar92467d32017-12-05 13:22:16 +01001053 if (mDMode == DM_INTEROP)
1054 {
1055 // GDI functions are used before this call. Keep using GDI.
1056 // (Switching to Direct2D causes terrible slowdown.)
1057 HBRUSH hbr = ::CreateSolidBrush(color);
1058 ::FillRect(mInteropHDC, rc, hbr);
1059 ::DeleteObject(HGDIOBJ(hbr));
1060 }
1061 else
1062 {
1063 SetDrawingMode(DM_DIRECTX);
1064 mRT->FillRectangle(
1065 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top),
1066 FLOAT(rc->right), FLOAT(rc->bottom)),
1067 SolidBrush(color));
1068 }
1069}
1070
1071 void
1072DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color)
1073{
1074 if (mDMode == DM_INTEROP)
1075 {
1076 // GDI functions are used before this call. Keep using GDI.
1077 // (Switching to Direct2D causes terrible slowdown.)
1078 HPEN hpen = ::CreatePen(PS_SOLID, 1, color);
1079 HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen));
1080 ::MoveToEx(mInteropHDC, x1, y1, NULL);
1081 ::LineTo(mInteropHDC, x2, y2);
1082 ::SelectObject(mInteropHDC, old_pen);
1083 ::DeleteObject(HGDIOBJ(hpen));
1084 }
1085 else
1086 {
1087 SetDrawingMode(DM_DIRECTX);
1088 mRT->DrawLine(
1089 D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f),
1090 D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f),
1091 SolidBrush(color));
1092 }
1093}
1094
1095 void
1096DWriteContext::SetPixel(int x, int y, COLORREF color)
1097{
1098 if (mDMode == DM_INTEROP)
1099 {
1100 // GDI functions are used before this call. Keep using GDI.
1101 // (Switching to Direct2D causes terrible slowdown.)
1102 ::SetPixel(mInteropHDC, x, y, color);
1103 }
1104 else
1105 {
1106 SetDrawingMode(DM_DIRECTX);
1107 // Direct2D doesn't have SetPixel API. Use DrawLine instead.
1108 mRT->DrawLine(
1109 D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f),
1110 D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f),
1111 SolidBrush(color));
1112 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001113}
1114
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001115 void
Bram Moolenaara338adc2018-01-31 20:51:47 +01001116DWriteContext::Scroll(int x, int y, const RECT *rc)
1117{
1118 SetDrawingMode(DM_DIRECTX);
1119 mRT->Flush();
1120
1121 D2D1_RECT_U srcRect;
1122 D2D1_POINT_2U destPoint;
1123 if (x >= 0)
1124 {
1125 srcRect.left = rc->left;
1126 srcRect.right = rc->right - x;
1127 destPoint.x = rc->left + x;
1128 }
1129 else
1130 {
1131 srcRect.left = rc->left - x;
1132 srcRect.right = rc->right;
1133 destPoint.x = rc->left;
1134 }
1135 if (y >= 0)
1136 {
1137 srcRect.top = rc->top;
1138 srcRect.bottom = rc->bottom - y;
1139 destPoint.y = rc->top + y;
1140 }
1141 else
1142 {
1143 srcRect.top = rc->top - y;
1144 srcRect.bottom = rc->bottom;
1145 destPoint.y = rc->top;
1146 }
1147 mBitmap->CopyFromRenderTarget(&destPoint, mRT, &srcRect);
1148
1149 D2D1_RECT_F destRect = {
1150 FLOAT(destPoint.x), FLOAT(destPoint.y),
1151 FLOAT(destPoint.x + srcRect.right - srcRect.left),
1152 FLOAT(destPoint.y + srcRect.bottom - srcRect.top)
1153 };
1154 mRT->DrawBitmap(mBitmap, destRect, 1.0F,
1155 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, destRect);
1156}
1157
1158 void
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001159DWriteContext::Flush()
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001160{
Bram Moolenaar92467d32017-12-05 13:22:16 +01001161 SetDrawingMode(DM_GDI);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001162}
1163
1164 void
1165DWriteContext::SetRenderingParams(
1166 const DWriteRenderingParams *params)
1167{
1168 if (mDWriteFactory == NULL)
1169 return;
1170
1171 IDWriteRenderingParams *renderingParams = NULL;
1172 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =
1173 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
1174 HRESULT hr;
1175 if (params != NULL)
1176 {
1177 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,
1178 params->enhancedContrast, params->clearTypeLevel,
1179 ToPixelGeometry(params->pixelGeometry),
1180 ToRenderingMode(params->renderingMode), &renderingParams);
1181 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);
1182 }
1183 else
1184 hr = mDWriteFactory->CreateRenderingParams(&renderingParams);
1185 if (SUCCEEDED(hr) && renderingParams != NULL)
1186 {
1187 SafeRelease(&mRenderingParams);
1188 mRenderingParams = renderingParams;
1189 mTextAntialiasMode = textAntialiasMode;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001190
1191 Flush();
1192 mRT->SetTextRenderingParams(mRenderingParams);
1193 mRT->SetTextAntialiasMode(mTextAntialiasMode);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001194 }
1195}
1196
1197 DWriteRenderingParams *
1198DWriteContext::GetRenderingParams(
1199 DWriteRenderingParams *params)
1200{
1201 if (params != NULL && mRenderingParams != NULL)
1202 {
1203 params->gamma = mRenderingParams->GetGamma();
1204 params->enhancedContrast = mRenderingParams->GetEnhancedContrast();
1205 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();
1206 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());
1207 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());
1208 params->textAntialiasMode = mTextAntialiasMode;
1209 }
1210 return params;
1211}
1212
1213////////////////////////////////////////////////////////////////////////////
1214// PUBLIC C INTERFACES
1215
1216 void
1217DWrite_Init(void)
1218{
1219#ifdef DYNAMIC_DIRECTX
1220 // Load libraries.
K.Takatad68b2fc2022-02-12 11:18:37 +00001221 hD2D1DLL = vimLoadLib("d2d1.dll");
1222 hDWriteDLL = vimLoadLib("dwrite.dll");
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001223 if (hD2D1DLL == NULL || hDWriteDLL == NULL)
1224 {
1225 DWrite_Final();
1226 return;
1227 }
1228 // Get address of procedures.
1229 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(
1230 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");
1231 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,
1232 "D2D1CreateFactory");
1233 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,
1234 "DWriteCreateFactory");
1235#endif
1236}
1237
1238 void
1239DWrite_Final(void)
1240{
1241#ifdef DYNAMIC_DIRECTX
1242 pGetUserDefaultLocaleName = NULL;
1243 pD2D1CreateFactory = NULL;
1244 pDWriteCreateFactory = NULL;
1245 unload(hDWriteDLL);
1246 unload(hD2D1DLL);
1247#endif
1248}
1249
1250 DWriteContext *
1251DWriteContext_Open(void)
1252{
1253#ifdef DYNAMIC_DIRECTX
1254 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL
1255 || pDWriteCreateFactory == NULL)
1256 return NULL;
1257#endif
1258 return new DWriteContext();
1259}
1260
1261 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001262DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001263{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001264 if (ctx != NULL)
1265 ctx->BindDC(hdc, rect);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001266}
1267
1268 void
1269DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
1270{
1271 if (ctx != NULL)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001272 ctx->SetFont(hFont);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001273}
1274
1275 void
1276DWriteContext_DrawText(
1277 DWriteContext *ctx,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001278 const WCHAR *text,
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001279 int len,
1280 int x,
1281 int y,
1282 int w,
1283 int h,
1284 int cellWidth,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001285 COLORREF color,
1286 UINT fuOptions,
Bram Moolenaar92467d32017-12-05 13:22:16 +01001287 const RECT *lprc,
1288 const INT *lpDx)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001289{
1290 if (ctx != NULL)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001291 ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
1292 fuOptions, lprc, lpDx);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001293}
1294
1295 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001296DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001297{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001298 if (ctx != NULL)
1299 ctx->FillRect(rc, color);
1300}
1301
1302 void
Bram Moolenaar92467d32017-12-05 13:22:16 +01001303DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2,
1304 COLORREF color)
1305{
1306 if (ctx != NULL)
1307 ctx->DrawLine(x1, y1, x2, y2, color);
1308}
1309
1310 void
1311DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color)
1312{
1313 if (ctx != NULL)
1314 ctx->SetPixel(x, y, color);
1315}
1316
1317 void
Bram Moolenaara338adc2018-01-31 20:51:47 +01001318DWriteContext_Scroll(DWriteContext *ctx, int x, int y, const RECT *rc)
1319{
1320 if (ctx != NULL)
1321 ctx->Scroll(x, y, rc);
1322}
1323
1324 void
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001325DWriteContext_Flush(DWriteContext *ctx)
1326{
1327 if (ctx != NULL)
1328 ctx->Flush();
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02001329}
1330
1331 void
1332DWriteContext_Close(DWriteContext *ctx)
1333{
1334 delete ctx;
1335}
1336
1337 void
1338DWriteContext_SetRenderingParams(
1339 DWriteContext *ctx,
1340 const DWriteRenderingParams *params)
1341{
1342 if (ctx != NULL)
1343 ctx->SetRenderingParams(params);
1344}
1345
1346 DWriteRenderingParams *
1347DWriteContext_GetRenderingParams(
1348 DWriteContext *ctx,
1349 DWriteRenderingParams *params)
1350{
1351 if (ctx != NULL)
1352 return ctx->GetRenderingParams(params);
1353 else
1354 return NULL;
1355}