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