6110
|
1 /* vi:set ts=8 sts=4 sw=4 noet: */
|
|
2 /*
|
|
3 * Author: MURAOKA Taro <koron.kaoriya@gmail.com>
|
|
4 *
|
|
5 * Contributors:
|
|
6 * - Ken Takata
|
|
7 *
|
|
8 * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
|
|
9 * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
|
|
10 */
|
|
11
|
|
12 #define WIN32_LEAN_AND_MEAN
|
|
13
|
|
14 #ifndef DYNAMIC_DIRECTX
|
|
15 # if WINVER < 0x0600
|
|
16 # error WINVER must be 0x0600 or above to use DirectWrite(DirectX)
|
|
17 # endif
|
|
18 #endif
|
|
19
|
|
20 #include <windows.h>
|
|
21 #include <crtdbg.h>
|
|
22 #include <assert.h>
|
|
23 #include <math.h>
|
|
24 #include <d2d1.h>
|
|
25 #include <d2d1helper.h>
|
|
26 #include <dwrite.h>
|
|
27
|
|
28 #include "gui_dwrite.h"
|
|
29
|
|
30 #ifdef __MINGW32__
|
|
31 # define __maybenull SAL__maybenull
|
|
32 # define __in SAL__in
|
|
33 # define __out SAL__out
|
|
34 #endif
|
|
35
|
|
36 #ifdef DYNAMIC_DIRECTX
|
|
37 extern "C" HINSTANCE vimLoadLib(char *name);
|
|
38
|
|
39 typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int);
|
|
40 typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE,
|
|
41 REFIID, const D2D1_FACTORY_OPTIONS *, void **);
|
|
42 typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE,
|
|
43 REFIID, IUnknown **);
|
|
44
|
|
45 static HINSTANCE hD2D1DLL = NULL;
|
|
46 static HINSTANCE hDWriteDLL = NULL;
|
|
47
|
|
48 static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL;
|
|
49 static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL;
|
|
50 static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL;
|
|
51
|
|
52 #define GetUserDefaultLocaleName (*pGetUserDefaultLocaleName)
|
|
53 #define D2D1CreateFactory (*pD2D1CreateFactory)
|
|
54 #define DWriteCreateFactory (*pDWriteCreateFactory)
|
|
55
|
|
56 static void
|
|
57 unload(HINSTANCE &hinst)
|
|
58 {
|
|
59 if (hinst != NULL)
|
|
60 {
|
|
61 FreeLibrary(hinst);
|
|
62 hinst = NULL;
|
|
63 }
|
|
64 }
|
|
65 #endif // DYNAMIC_DIRECTX
|
|
66
|
|
67 template <class T> inline void SafeRelease(T **ppT)
|
|
68 {
|
|
69 if (*ppT)
|
|
70 {
|
|
71 (*ppT)->Release();
|
|
72 *ppT = NULL;
|
|
73 }
|
|
74 }
|
|
75
|
|
76 struct GdiTextRendererContext
|
|
77 {
|
|
78 // const fields.
|
|
79 COLORREF color;
|
|
80 FLOAT cellWidth;
|
|
81
|
|
82 // working fields.
|
|
83 FLOAT offsetX;
|
|
84 };
|
|
85
|
|
86 static DWRITE_PIXEL_GEOMETRY
|
|
87 ToPixelGeometry(int value)
|
|
88 {
|
|
89 switch (value)
|
|
90 {
|
|
91 default:
|
|
92 case 0:
|
|
93 return DWRITE_PIXEL_GEOMETRY_FLAT;
|
|
94 case 1:
|
|
95 return DWRITE_PIXEL_GEOMETRY_RGB;
|
|
96 case 2:
|
|
97 return DWRITE_PIXEL_GEOMETRY_BGR;
|
|
98 }
|
|
99 }
|
|
100
|
|
101 static int
|
|
102 ToInt(DWRITE_PIXEL_GEOMETRY value)
|
|
103 {
|
|
104 switch (value)
|
|
105 {
|
|
106 case DWRITE_PIXEL_GEOMETRY_FLAT:
|
|
107 return 0;
|
|
108 case DWRITE_PIXEL_GEOMETRY_RGB:
|
|
109 return 1;
|
|
110 case DWRITE_PIXEL_GEOMETRY_BGR:
|
|
111 return 2;
|
|
112 default:
|
|
113 return -1;
|
|
114 }
|
|
115 }
|
|
116
|
|
117 static DWRITE_RENDERING_MODE
|
|
118 ToRenderingMode(int value)
|
|
119 {
|
|
120 switch (value)
|
|
121 {
|
|
122 default:
|
|
123 case 0:
|
|
124 return DWRITE_RENDERING_MODE_DEFAULT;
|
|
125 case 1:
|
|
126 return DWRITE_RENDERING_MODE_ALIASED;
|
|
127 case 2:
|
|
128 return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
|
|
129 case 3:
|
|
130 return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL;
|
|
131 case 4:
|
|
132 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
|
|
133 case 5:
|
|
134 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
|
|
135 case 6:
|
|
136 return DWRITE_RENDERING_MODE_OUTLINE;
|
|
137 }
|
|
138 }
|
|
139
|
|
140 static D2D1_TEXT_ANTIALIAS_MODE
|
|
141 ToTextAntialiasMode(int value)
|
|
142 {
|
|
143 switch (value)
|
|
144 {
|
|
145 default:
|
|
146 case 0:
|
|
147 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
|
|
148 case 1:
|
|
149 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
|
|
150 case 2:
|
|
151 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
|
|
152 case 3:
|
|
153 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
|
|
154 }
|
|
155 }
|
|
156
|
|
157 static int
|
|
158 ToInt(DWRITE_RENDERING_MODE value)
|
|
159 {
|
|
160 switch (value)
|
|
161 {
|
|
162 case DWRITE_RENDERING_MODE_DEFAULT:
|
|
163 return 0;
|
|
164 case DWRITE_RENDERING_MODE_ALIASED:
|
|
165 return 1;
|
|
166 case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
|
|
167 return 2;
|
|
168 case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
|
|
169 return 3;
|
|
170 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
|
|
171 return 4;
|
|
172 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
|
|
173 return 5;
|
|
174 case DWRITE_RENDERING_MODE_OUTLINE:
|
|
175 return 6;
|
|
176 default:
|
|
177 return -1;
|
|
178 }
|
|
179 }
|
|
180
|
|
181 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
|
|
182 {
|
|
183 private:
|
|
184 FLOAT mDelta;
|
|
185 FLOAT *mAdjustedAdvances;
|
|
186
|
|
187 public:
|
|
188 AdjustedGlyphRun(
|
|
189 const DWRITE_GLYPH_RUN *glyphRun,
|
|
190 FLOAT cellWidth) :
|
|
191 DWRITE_GLYPH_RUN(*glyphRun),
|
|
192 mDelta(0.0f),
|
|
193 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
|
|
194 {
|
|
195 assert(cellWidth != 0.0f);
|
|
196 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
|
|
197 {
|
|
198 FLOAT orig = glyphRun->glyphAdvances[i];
|
|
199 FLOAT adjusted = adjustToCell(orig, cellWidth);
|
|
200 mAdjustedAdvances[i] = adjusted;
|
|
201 mDelta += adjusted - orig;
|
|
202 }
|
|
203 glyphAdvances = mAdjustedAdvances;
|
|
204 }
|
|
205
|
|
206 ~AdjustedGlyphRun(void)
|
|
207 {
|
|
208 delete[] mAdjustedAdvances;
|
|
209 }
|
|
210
|
|
211 FLOAT getDelta(void) const
|
|
212 {
|
|
213 return mDelta;
|
|
214 }
|
|
215
|
|
216 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
|
|
217 {
|
|
218 int cellCount = (int)floor(value / cellWidth + 0.5f);
|
|
219 if (cellCount < 1)
|
|
220 cellCount = 1;
|
|
221 return cellCount * cellWidth;
|
|
222 }
|
|
223 };
|
|
224
|
|
225 class GdiTextRenderer : public IDWriteTextRenderer
|
|
226 {
|
|
227 public:
|
|
228 GdiTextRenderer(
|
|
229 IDWriteBitmapRenderTarget* bitmapRenderTarget,
|
|
230 IDWriteRenderingParams* renderingParams) :
|
|
231 cRefCount_(0),
|
|
232 pRenderTarget_(bitmapRenderTarget),
|
|
233 pRenderingParams_(renderingParams)
|
|
234 {
|
|
235 pRenderTarget_->AddRef();
|
|
236 pRenderingParams_->AddRef();
|
|
237 AddRef();
|
|
238 }
|
|
239
|
|
240 ~GdiTextRenderer()
|
|
241 {
|
|
242 SafeRelease(&pRenderTarget_);
|
|
243 SafeRelease(&pRenderingParams_);
|
|
244 }
|
|
245
|
|
246 IFACEMETHOD(IsPixelSnappingDisabled)(
|
|
247 __maybenull void* clientDrawingContext,
|
|
248 __out BOOL* isDisabled)
|
|
249 {
|
|
250 *isDisabled = FALSE;
|
|
251 return S_OK;
|
|
252 }
|
|
253
|
|
254 IFACEMETHOD(GetCurrentTransform)(
|
|
255 __maybenull void* clientDrawingContext,
|
|
256 __out DWRITE_MATRIX* transform)
|
|
257 {
|
|
258 //forward the render target's transform
|
|
259 pRenderTarget_->GetCurrentTransform(transform);
|
|
260 return S_OK;
|
|
261 }
|
|
262
|
|
263 IFACEMETHOD(GetPixelsPerDip)(
|
|
264 __maybenull void* clientDrawingContext,
|
|
265 __out FLOAT* pixelsPerDip)
|
|
266 {
|
|
267 *pixelsPerDip = pRenderTarget_->GetPixelsPerDip();
|
|
268 return S_OK;
|
|
269 }
|
|
270
|
|
271 IFACEMETHOD(DrawGlyphRun)(
|
|
272 __maybenull void* clientDrawingContext,
|
|
273 FLOAT baselineOriginX,
|
|
274 FLOAT baselineOriginY,
|
|
275 DWRITE_MEASURING_MODE measuringMode,
|
|
276 __in DWRITE_GLYPH_RUN const* glyphRun,
|
|
277 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
|
|
278 IUnknown* clientDrawingEffect)
|
|
279 {
|
|
280 HRESULT hr = S_OK;
|
|
281
|
|
282 GdiTextRendererContext *context =
|
|
283 reinterpret_cast<GdiTextRendererContext*>(clientDrawingContext);
|
|
284
|
|
285 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth);
|
|
286
|
|
287 // Pass on the drawing call to the render target to do the real work.
|
|
288 RECT dirtyRect = {0};
|
|
289
|
|
290 hr = pRenderTarget_->DrawGlyphRun(
|
|
291 baselineOriginX + context->offsetX,
|
|
292 baselineOriginY,
|
|
293 measuringMode,
|
|
294 &adjustedGlyphRun,
|
|
295 pRenderingParams_,
|
|
296 context->color,
|
|
297 &dirtyRect);
|
|
298
|
|
299 context->offsetX += adjustedGlyphRun.getDelta();
|
|
300
|
|
301 return hr;
|
|
302 }
|
|
303
|
|
304 IFACEMETHOD(DrawUnderline)(
|
|
305 __maybenull void* clientDrawingContext,
|
|
306 FLOAT baselineOriginX,
|
|
307 FLOAT baselineOriginY,
|
|
308 __in DWRITE_UNDERLINE const* underline,
|
|
309 IUnknown* clientDrawingEffect)
|
|
310 {
|
|
311 return E_NOTIMPL;
|
|
312 }
|
|
313
|
|
314 IFACEMETHOD(DrawStrikethrough)(
|
|
315 __maybenull void* clientDrawingContext,
|
|
316 FLOAT baselineOriginX,
|
|
317 FLOAT baselineOriginY,
|
|
318 __in DWRITE_STRIKETHROUGH const* strikethrough,
|
|
319 IUnknown* clientDrawingEffect)
|
|
320 {
|
|
321 return E_NOTIMPL;
|
|
322 }
|
|
323
|
|
324 IFACEMETHOD(DrawInlineObject)(
|
|
325 __maybenull void* clientDrawingContext,
|
|
326 FLOAT originX,
|
|
327 FLOAT originY,
|
|
328 IDWriteInlineObject* inlineObject,
|
|
329 BOOL isSideways,
|
|
330 BOOL isRightToLeft,
|
|
331 IUnknown* clientDrawingEffect)
|
|
332 {
|
|
333 return E_NOTIMPL;
|
|
334 }
|
|
335
|
|
336 public:
|
|
337 IFACEMETHOD_(unsigned long, AddRef) ()
|
|
338 {
|
|
339 return InterlockedIncrement(&cRefCount_);
|
|
340 }
|
|
341
|
|
342 IFACEMETHOD_(unsigned long, Release) ()
|
|
343 {
|
|
344 long newCount = InterlockedDecrement(&cRefCount_);
|
|
345
|
|
346 if (newCount == 0)
|
|
347 {
|
|
348 delete this;
|
|
349 return 0;
|
|
350 }
|
|
351 return newCount;
|
|
352 }
|
|
353
|
|
354 IFACEMETHOD(QueryInterface)(
|
|
355 IID const& riid,
|
|
356 void** ppvObject)
|
|
357 {
|
|
358 if (__uuidof(IDWriteTextRenderer) == riid)
|
|
359 {
|
|
360 *ppvObject = this;
|
|
361 }
|
|
362 else if (__uuidof(IDWritePixelSnapping) == riid)
|
|
363 {
|
|
364 *ppvObject = this;
|
|
365 }
|
|
366 else if (__uuidof(IUnknown) == riid)
|
|
367 {
|
|
368 *ppvObject = this;
|
|
369 }
|
|
370 else
|
|
371 {
|
|
372 *ppvObject = NULL;
|
|
373 return E_FAIL;
|
|
374 }
|
|
375
|
|
376 return S_OK;
|
|
377 }
|
|
378
|
|
379 private:
|
6120
|
380 long cRefCount_;
|
6110
|
381 IDWriteBitmapRenderTarget* pRenderTarget_;
|
|
382 IDWriteRenderingParams* pRenderingParams_;
|
|
383 };
|
|
384
|
|
385 struct DWriteContext {
|
|
386 FLOAT mDpiScaleX;
|
|
387 FLOAT mDpiScaleY;
|
|
388 bool mDrawing;
|
|
389
|
|
390 ID2D1Factory *mD2D1Factory;
|
|
391
|
|
392 ID2D1DCRenderTarget *mRT;
|
|
393 ID2D1SolidColorBrush *mBrush;
|
|
394
|
|
395 IDWriteFactory *mDWriteFactory;
|
|
396 IDWriteGdiInterop *mGdiInterop;
|
|
397 IDWriteRenderingParams *mRenderingParams;
|
|
398 IDWriteTextFormat *mTextFormat;
|
|
399
|
|
400 HFONT mLastHFont;
|
|
401 DWRITE_FONT_WEIGHT mFontWeight;
|
|
402 DWRITE_FONT_STYLE mFontStyle;
|
|
403
|
|
404 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
|
|
405
|
|
406 // METHODS
|
|
407
|
|
408 DWriteContext();
|
|
409
|
|
410 virtual ~DWriteContext();
|
|
411
|
|
412 HRESULT SetLOGFONT(const LOGFONTW &logFont, float fontSize);
|
|
413
|
|
414 void SetFont(HFONT hFont);
|
|
415
|
|
416 void SetFont(const LOGFONTW &logFont);
|
|
417
|
|
418 void DrawText(HDC hdc, const WCHAR* text, int len,
|
|
419 int x, int y, int w, int h, int cellWidth, COLORREF color);
|
|
420
|
|
421 float PixelsToDipsX(int x);
|
|
422
|
|
423 float PixelsToDipsY(int y);
|
|
424
|
|
425 void SetRenderingParams(
|
|
426 const DWriteRenderingParams *params);
|
|
427
|
|
428 DWriteRenderingParams *GetRenderingParams(
|
|
429 DWriteRenderingParams *params);
|
|
430 };
|
|
431
|
|
432 DWriteContext::DWriteContext() :
|
|
433 mDpiScaleX(1.f),
|
|
434 mDpiScaleY(1.f),
|
|
435 mDrawing(false),
|
|
436 mD2D1Factory(NULL),
|
|
437 mRT(NULL),
|
|
438 mBrush(NULL),
|
|
439 mDWriteFactory(NULL),
|
|
440 mGdiInterop(NULL),
|
|
441 mRenderingParams(NULL),
|
|
442 mTextFormat(NULL),
|
|
443 mLastHFont(NULL),
|
|
444 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
|
|
445 mFontStyle(DWRITE_FONT_STYLE_NORMAL),
|
|
446 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
|
|
447 {
|
|
448 HRESULT hr;
|
|
449
|
|
450 HDC screen = ::GetDC(0);
|
|
451 mDpiScaleX = ::GetDeviceCaps(screen, LOGPIXELSX) / 96.0f;
|
|
452 mDpiScaleY = ::GetDeviceCaps(screen, LOGPIXELSY) / 96.0f;
|
|
453 ::ReleaseDC(0, screen);
|
|
454
|
|
455 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
|
456 __uuidof(ID2D1Factory), NULL,
|
|
457 reinterpret_cast<void**>(&mD2D1Factory));
|
|
458 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
|
|
459
|
|
460 if (SUCCEEDED(hr))
|
|
461 {
|
|
462 D2D1_RENDER_TARGET_PROPERTIES props = {
|
|
463 D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
|
464 { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },
|
|
465 0, 0,
|
|
466 D2D1_RENDER_TARGET_USAGE_NONE,
|
|
467 D2D1_FEATURE_LEVEL_DEFAULT
|
|
468 };
|
|
469 hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);
|
|
470 _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);
|
|
471 }
|
|
472
|
|
473 if (SUCCEEDED(hr))
|
|
474 {
|
|
475 hr = mRT->CreateSolidColorBrush(
|
|
476 D2D1::ColorF(D2D1::ColorF::Black),
|
|
477 &mBrush);
|
|
478 _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);
|
|
479 }
|
|
480
|
|
481 if (SUCCEEDED(hr))
|
|
482 {
|
|
483 hr = DWriteCreateFactory(
|
|
484 DWRITE_FACTORY_TYPE_SHARED,
|
|
485 __uuidof(IDWriteFactory),
|
|
486 reinterpret_cast<IUnknown**>(&mDWriteFactory));
|
|
487 _RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,
|
|
488 mDWriteFactory);
|
|
489 }
|
|
490
|
|
491 if (SUCCEEDED(hr))
|
|
492 {
|
|
493 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
|
|
494 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
|
|
495 }
|
|
496
|
|
497 if (SUCCEEDED(hr))
|
|
498 {
|
|
499 hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);
|
|
500 _RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,
|
|
501 mRenderingParams);
|
|
502 }
|
|
503 }
|
|
504
|
|
505 DWriteContext::~DWriteContext()
|
|
506 {
|
|
507 SafeRelease(&mTextFormat);
|
|
508 SafeRelease(&mRenderingParams);
|
|
509 SafeRelease(&mGdiInterop);
|
|
510 SafeRelease(&mDWriteFactory);
|
|
511 SafeRelease(&mBrush);
|
|
512 SafeRelease(&mRT);
|
|
513 SafeRelease(&mD2D1Factory);
|
|
514 }
|
|
515
|
|
516 HRESULT
|
|
517 DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize)
|
|
518 {
|
|
519 // Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspx
|
|
520 HRESULT hr = S_OK;
|
|
521
|
|
522 IDWriteFont *font = NULL;
|
|
523 IDWriteFontFamily *fontFamily = NULL;
|
|
524 IDWriteLocalizedStrings *localizedFamilyNames = NULL;
|
|
525
|
|
526 if (SUCCEEDED(hr))
|
|
527 {
|
|
528 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
|
|
529 }
|
|
530
|
|
531 // Get the font family to which this font belongs.
|
|
532 if (SUCCEEDED(hr))
|
|
533 {
|
|
534 hr = font->GetFontFamily(&fontFamily);
|
|
535 }
|
|
536
|
|
537 // Get the family names. This returns an object that encapsulates one or
|
|
538 // more names with the same meaning but in different languages.
|
|
539 if (SUCCEEDED(hr))
|
|
540 {
|
|
541 hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
|
|
542 }
|
|
543
|
|
544 // Get the family name at index zero. If we were going to display the name
|
|
545 // we'd want to try to find one that matched the use locale, but for
|
|
546 // purposes of creating a text format object any language will do.
|
|
547
|
|
548 wchar_t familyName[100];
|
|
549 if (SUCCEEDED(hr))
|
|
550 {
|
|
551 hr = localizedFamilyNames->GetString(0, familyName,
|
|
552 ARRAYSIZE(familyName));
|
|
553 }
|
|
554
|
|
555 if (SUCCEEDED(hr))
|
|
556 {
|
|
557 // If no font size was passed in use the lfHeight of the LOGFONT.
|
|
558 if (fontSize == 0)
|
|
559 {
|
|
560 // Convert from pixels to DIPs.
|
|
561 fontSize = PixelsToDipsY(logFont.lfHeight);
|
|
562 if (fontSize < 0)
|
|
563 {
|
|
564 // Negative lfHeight represents the size of the em unit.
|
|
565 fontSize = -fontSize;
|
|
566 }
|
|
567 else
|
|
568 {
|
|
569 // Positive lfHeight represents the cell height (ascent +
|
|
570 // descent).
|
|
571 DWRITE_FONT_METRICS fontMetrics;
|
|
572 font->GetMetrics(&fontMetrics);
|
|
573
|
|
574 // Convert the cell height (ascent + descent) from design units
|
|
575 // to ems.
|
|
576 float cellHeight = static_cast<float>(
|
|
577 fontMetrics.ascent + fontMetrics.descent)
|
|
578 / fontMetrics.designUnitsPerEm;
|
|
579
|
|
580 // Divide the font size by the cell height to get the font em
|
|
581 // size.
|
|
582 fontSize /= cellHeight;
|
|
583 }
|
|
584 }
|
|
585 }
|
|
586
|
|
587 // The text format includes a locale name. Ideally, this would be the
|
|
588 // language of the text, which may or may not be the same as the primary
|
|
589 // language of the user. However, for our purposes the user locale will do.
|
|
590 wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
|
|
591 if (SUCCEEDED(hr))
|
|
592 {
|
|
593 if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
|
|
594 hr = HRESULT_FROM_WIN32(GetLastError());
|
|
595 }
|
|
596
|
|
597 if (SUCCEEDED(hr))
|
|
598 {
|
|
599 // Create the text format object.
|
|
600 hr = mDWriteFactory->CreateTextFormat(
|
|
601 familyName,
|
|
602 NULL, // no custom font collection
|
|
603 font->GetWeight(),
|
|
604 font->GetStyle(),
|
|
605 font->GetStretch(),
|
|
606 fontSize,
|
|
607 localeName,
|
|
608 &mTextFormat);
|
|
609 }
|
|
610
|
|
611 if (SUCCEEDED(hr))
|
|
612 {
|
|
613 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
|
|
614 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
|
|
615 : DWRITE_FONT_STYLE_NORMAL;
|
|
616 }
|
|
617
|
|
618 SafeRelease(&localizedFamilyNames);
|
|
619 SafeRelease(&fontFamily);
|
|
620 SafeRelease(&font);
|
|
621
|
|
622 return hr;
|
|
623 }
|
|
624
|
|
625 void
|
|
626 DWriteContext::SetFont(HFONT hFont)
|
|
627 {
|
|
628 if (mLastHFont != hFont)
|
|
629 {
|
|
630 LOGFONTW lf;
|
|
631 if (GetObjectW(hFont, sizeof(lf), &lf))
|
|
632 {
|
|
633 SetFont(lf);
|
|
634 mLastHFont = hFont;
|
|
635 }
|
|
636 }
|
|
637 }
|
|
638
|
|
639 void
|
|
640 DWriteContext::SetFont(const LOGFONTW &logFont)
|
|
641 {
|
|
642 SafeRelease(&mTextFormat);
|
|
643 mLastHFont = NULL;
|
|
644
|
|
645 HRESULT hr = SetLOGFONT(logFont, 0.f);
|
|
646
|
|
647 if (SUCCEEDED(hr))
|
|
648 hr = mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
|
|
649
|
|
650 if (SUCCEEDED(hr))
|
|
651 hr = mTextFormat->SetParagraphAlignment(
|
|
652 DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
|
|
653
|
|
654 if (SUCCEEDED(hr))
|
|
655 hr = mTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
|
|
656 }
|
|
657
|
|
658 void
|
|
659 DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len,
|
|
660 int x, int y, int w, int h, int cellWidth, COLORREF color)
|
|
661 {
|
|
662 HRESULT hr = S_OK;
|
|
663 IDWriteBitmapRenderTarget *bmpRT = NULL;
|
|
664
|
|
665 // Skip when any fonts are not set.
|
|
666 if (mTextFormat == NULL)
|
|
667 return;
|
|
668
|
|
669 // Check possibility of zero divided error.
|
|
670 if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f)
|
|
671 return;
|
|
672
|
|
673 if (SUCCEEDED(hr))
|
|
674 hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT);
|
|
675
|
|
676 if (SUCCEEDED(hr))
|
|
677 {
|
|
678 IDWriteTextLayout *textLayout = NULL;
|
|
679
|
|
680 HDC memdc = bmpRT->GetMemoryDC();
|
|
681 BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY);
|
|
682
|
|
683 hr = mDWriteFactory->CreateGdiCompatibleTextLayout(
|
|
684 text, len, mTextFormat, PixelsToDipsX(w),
|
|
685 PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout);
|
|
686
|
|
687 if (SUCCEEDED(hr))
|
|
688 {
|
|
689 DWRITE_TEXT_RANGE textRange = { 0, len };
|
|
690 textLayout->SetFontWeight(mFontWeight, textRange);
|
|
691 textLayout->SetFontStyle(mFontStyle, textRange);
|
|
692 }
|
|
693
|
|
694 if (SUCCEEDED(hr))
|
|
695 {
|
|
696 GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT,
|
|
697 mRenderingParams);
|
|
698 GdiTextRendererContext data = {
|
|
699 color,
|
|
700 PixelsToDipsX(cellWidth),
|
|
701 0.0f
|
|
702 };
|
|
703 textLayout->Draw(&data, renderer, 0, 0);
|
|
704 SafeRelease(&renderer);
|
|
705 }
|
|
706
|
|
707 BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY);
|
|
708
|
|
709 SafeRelease(&textLayout);
|
|
710 }
|
|
711
|
|
712 SafeRelease(&bmpRT);
|
|
713 }
|
|
714
|
|
715 float
|
|
716 DWriteContext::PixelsToDipsX(int x)
|
|
717 {
|
|
718 return x / mDpiScaleX;
|
|
719 }
|
|
720
|
|
721 float
|
|
722 DWriteContext::PixelsToDipsY(int y)
|
|
723 {
|
|
724 return y / mDpiScaleY;
|
|
725 }
|
|
726
|
|
727 void
|
|
728 DWriteContext::SetRenderingParams(
|
|
729 const DWriteRenderingParams *params)
|
|
730 {
|
|
731 if (mDWriteFactory == NULL)
|
|
732 return;
|
|
733
|
|
734 IDWriteRenderingParams *renderingParams = NULL;
|
|
735 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =
|
|
736 D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
|
|
737 HRESULT hr;
|
|
738 if (params != NULL)
|
|
739 {
|
|
740 hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,
|
|
741 params->enhancedContrast, params->clearTypeLevel,
|
|
742 ToPixelGeometry(params->pixelGeometry),
|
|
743 ToRenderingMode(params->renderingMode), &renderingParams);
|
|
744 textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);
|
|
745 }
|
|
746 else
|
|
747 hr = mDWriteFactory->CreateRenderingParams(&renderingParams);
|
|
748 if (SUCCEEDED(hr) && renderingParams != NULL)
|
|
749 {
|
|
750 SafeRelease(&mRenderingParams);
|
|
751 mRenderingParams = renderingParams;
|
|
752 mTextAntialiasMode = textAntialiasMode;
|
|
753 }
|
|
754 }
|
|
755
|
|
756 DWriteRenderingParams *
|
|
757 DWriteContext::GetRenderingParams(
|
|
758 DWriteRenderingParams *params)
|
|
759 {
|
|
760 if (params != NULL && mRenderingParams != NULL)
|
|
761 {
|
|
762 params->gamma = mRenderingParams->GetGamma();
|
|
763 params->enhancedContrast = mRenderingParams->GetEnhancedContrast();
|
|
764 params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();
|
|
765 params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());
|
|
766 params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());
|
|
767 params->textAntialiasMode = mTextAntialiasMode;
|
|
768 }
|
|
769 return params;
|
|
770 }
|
|
771
|
|
772 ////////////////////////////////////////////////////////////////////////////
|
|
773 // PUBLIC C INTERFACES
|
|
774
|
|
775 void
|
|
776 DWrite_Init(void)
|
|
777 {
|
|
778 #ifdef DYNAMIC_DIRECTX
|
|
779 // Load libraries.
|
|
780 hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll"));
|
|
781 hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll"));
|
|
782 if (hD2D1DLL == NULL || hDWriteDLL == NULL)
|
|
783 {
|
|
784 DWrite_Final();
|
|
785 return;
|
|
786 }
|
|
787 // Get address of procedures.
|
|
788 pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(
|
|
789 GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");
|
|
790 pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,
|
|
791 "D2D1CreateFactory");
|
|
792 pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,
|
|
793 "DWriteCreateFactory");
|
|
794 #endif
|
|
795 }
|
|
796
|
|
797 void
|
|
798 DWrite_Final(void)
|
|
799 {
|
|
800 #ifdef DYNAMIC_DIRECTX
|
|
801 pGetUserDefaultLocaleName = NULL;
|
|
802 pD2D1CreateFactory = NULL;
|
|
803 pDWriteCreateFactory = NULL;
|
|
804 unload(hDWriteDLL);
|
|
805 unload(hD2D1DLL);
|
|
806 #endif
|
|
807 }
|
|
808
|
|
809 DWriteContext *
|
|
810 DWriteContext_Open(void)
|
|
811 {
|
|
812 #ifdef DYNAMIC_DIRECTX
|
|
813 if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL
|
|
814 || pDWriteCreateFactory == NULL)
|
|
815 return NULL;
|
|
816 #endif
|
|
817 return new DWriteContext();
|
|
818 }
|
|
819
|
|
820 void
|
|
821 DWriteContext_BeginDraw(DWriteContext *ctx)
|
|
822 {
|
|
823 if (ctx != NULL && ctx->mRT != NULL)
|
|
824 {
|
|
825 ctx->mRT->BeginDraw();
|
|
826 ctx->mRT->SetTransform(D2D1::IdentityMatrix());
|
|
827 ctx->mDrawing = true;
|
|
828 }
|
|
829 }
|
|
830
|
|
831 void
|
|
832 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect)
|
|
833 {
|
|
834 if (ctx != NULL && ctx->mRT != NULL)
|
|
835 {
|
|
836 ctx->mRT->BindDC(hdc, rect);
|
|
837 ctx->mRT->SetTextAntialiasMode(ctx->mTextAntialiasMode);
|
|
838 }
|
|
839 }
|
|
840
|
|
841 void
|
|
842 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
|
|
843 {
|
|
844 if (ctx != NULL)
|
|
845 {
|
|
846 ctx->SetFont(hFont);
|
|
847 }
|
|
848 }
|
|
849
|
|
850 void
|
|
851 DWriteContext_DrawText(
|
|
852 DWriteContext *ctx,
|
|
853 HDC hdc,
|
|
854 const WCHAR* text,
|
|
855 int len,
|
|
856 int x,
|
|
857 int y,
|
|
858 int w,
|
|
859 int h,
|
|
860 int cellWidth,
|
|
861 COLORREF color)
|
|
862 {
|
|
863 if (ctx != NULL)
|
|
864 ctx->DrawText(hdc, text, len, x, y, w, h, cellWidth, color);
|
|
865 }
|
|
866
|
|
867 void
|
|
868 DWriteContext_EndDraw(DWriteContext *ctx)
|
|
869 {
|
|
870 if (ctx != NULL && ctx->mRT != NULL)
|
|
871 {
|
|
872 ctx->mRT->EndDraw();
|
|
873 ctx->mDrawing = false;
|
|
874 }
|
|
875 }
|
|
876
|
|
877 void
|
|
878 DWriteContext_Close(DWriteContext *ctx)
|
|
879 {
|
|
880 delete ctx;
|
|
881 }
|
|
882
|
|
883 void
|
|
884 DWriteContext_SetRenderingParams(
|
|
885 DWriteContext *ctx,
|
|
886 const DWriteRenderingParams *params)
|
|
887 {
|
|
888 if (ctx != NULL)
|
|
889 ctx->SetRenderingParams(params);
|
|
890 }
|
|
891
|
|
892 DWriteRenderingParams *
|
|
893 DWriteContext_GetRenderingParams(
|
|
894 DWriteContext *ctx,
|
|
895 DWriteRenderingParams *params)
|
|
896 {
|
|
897 if (ctx != NULL)
|
|
898 return ctx->GetRenderingParams(params);
|
|
899 else
|
|
900 return NULL;
|
|
901 }
|