diff --git a/core/Base/Renderer.cpp b/core/Base/Renderer.cpp index aca67cbc..36738110 100644 --- a/core/Base/Renderer.cpp +++ b/core/Base/Renderer.cpp @@ -3,12 +3,15 @@ #include "..\e2dnode.h" static bool s_bShowFps = false; +static float s_fDpiScaleX = 0; +static float s_fDpiScaleY = 0; static IDWriteTextFormat * s_pTextFormat = nullptr; static ID2D1Factory * s_pDirect2dFactory = nullptr; static ID2D1HwndRenderTarget * s_pRenderTarget = nullptr; static ID2D1SolidColorBrush * s_pSolidBrush = nullptr; static IWICImagingFactory * s_pIWICFactory = nullptr; static IDWriteFactory * s_pDWriteFactory = nullptr; +static e2d::CustomTextRenderer * s_pTextRenderer = nullptr; static D2D1_COLOR_F s_nClearColor = D2D1::ColorF(D2D1::ColorF::Black); @@ -16,7 +19,7 @@ bool e2d::Renderer::__createDeviceIndependentResources() { // 创建设备无关资源,它们的生命周期和程序的时长相同 HRESULT hr = D2D1CreateFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, + D2D1_FACTORY_TYPE_SINGLE_THREADED, &s_pDirect2dFactory ); @@ -46,21 +49,27 @@ bool e2d::Renderer::__createDeviceIndependentResources() ASSERT(SUCCEEDED(hr), "Create IDWriteFactory Failed!"); } + if (SUCCEEDED(hr)) + { + // 工厂将返回当前的系统 DPI,这个值也将用来创建窗口 + Renderer::getID2D1Factory()->GetDesktopDpi(&s_fDpiScaleX, &s_fDpiScaleY); + } + if (SUCCEEDED(hr)) { // 创建文本格式化对象 hr = s_pDWriteFactory->CreateTextFormat( L"", NULL, - DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, - 18, + 22, L"", &s_pTextFormat ); - if (s_pTextFormat) + if (SUCCEEDED(hr)) { s_pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); } @@ -72,7 +81,7 @@ bool e2d::Renderer::__createDeviceIndependentResources() bool e2d::Renderer::__createDeviceResources() { HRESULT hr = S_OK; - + if (!s_pRenderTarget) { HWND hWnd = Window::getHWnd(); @@ -107,8 +116,18 @@ bool e2d::Renderer::__createDeviceResources() ); ASSERT(SUCCEEDED(hr), "Create ID2D1SolidColorBrush Failed!"); } + + if (SUCCEEDED(hr)) + { + // 创建自定义的文字渲染器 + s_pTextRenderer = new (std::nothrow) CustomTextRenderer( + s_pDirect2dFactory, + s_pRenderTarget, + s_pSolidBrush + ); + } } - + return SUCCEEDED(hr); } @@ -116,6 +135,7 @@ void e2d::Renderer::__discardDeviceResources() { SafeReleaseInterface(&s_pRenderTarget); SafeReleaseInterface(&s_pSolidBrush); + SafeReleaseInterface(&s_pTextRenderer); } void e2d::Renderer::__discardResources() @@ -124,6 +144,7 @@ void e2d::Renderer::__discardResources() SafeReleaseInterface(&s_pDirect2dFactory); SafeReleaseInterface(&s_pRenderTarget); SafeReleaseInterface(&s_pSolidBrush); + SafeReleaseInterface(&s_pTextRenderer); SafeReleaseInterface(&s_pIWICFactory); SafeReleaseInterface(&s_pDWriteFactory); } @@ -148,7 +169,7 @@ void e2d::Renderer::__render() { static int s_nRenderTimes = 0; static double s_fLastRenderTime = 0; - static e2d::String s_sFpsText = L""; + static String s_sFpsText; s_nRenderTimes++; @@ -160,9 +181,31 @@ void e2d::Renderer::__render() s_nRenderTimes = 0; } - s_pSolidBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White)); - s_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); - s_pRenderTarget->DrawTextW(s_sFpsText, (UINT32)s_sFpsText.getLength(), s_pTextFormat, D2D1::RectF(), s_pSolidBrush); + IDWriteTextLayout * pTextLayout = nullptr; + + hr = s_pDWriteFactory->CreateTextLayout( + s_sFpsText, + (UINT32)s_sFpsText.getLength(), + s_pTextFormat, + 0, + 0, + &pTextLayout + ); + + if (SUCCEEDED(hr)) + { + s_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); + s_pTextRenderer->SetTextStyle( + D2D1::ColorF(D2D1::ColorF::White), + D2D1::ColorF(D2D1::ColorF::Black), + 2.0f, + 1.0f + ); + + pTextLayout->Draw(NULL, s_pTextRenderer, 10, 0); + + SafeReleaseInterface(&pTextLayout); + } } // 终止渲染 @@ -195,6 +238,16 @@ void e2d::Renderer::showFps(bool show) s_bShowFps = show; } +float e2d::Renderer::getDpiScaleX() +{ + return s_fDpiScaleX; +} + +float e2d::Renderer::getDpiScaleY() +{ + return s_fDpiScaleY; +} + ID2D1Factory * e2d::Renderer::getID2D1Factory() { return s_pDirect2dFactory; @@ -219,3 +272,8 @@ IDWriteFactory * e2d::Renderer::getIDWriteFactory() { return s_pDWriteFactory; } + +e2d::CustomTextRenderer * e2d::Renderer::getCustomTextRenderer() +{ + return s_pTextRenderer; +} diff --git a/core/Base/Window.cpp b/core/Base/Window.cpp index a7ce19f0..d92623fb 100644 --- a/core/Base/Window.cpp +++ b/core/Base/Window.cpp @@ -29,10 +29,8 @@ bool e2d::Window::__init() // 因为 CreateWindow 函数使用的是像素大小,获取系统的 DPI 以使它 // 适应窗口缩放 - float dpiX, dpiY; - - // 工厂将返回当前的系统 DPI,这个值也将用来创建窗口 - Renderer::getID2D1Factory()->GetDesktopDpi(&dpiX, &dpiY); + float dpiX = Renderer::getDpiScaleX(); + float dpiY = Renderer::getDpiScaleY(); UINT nWidth = static_cast(ceil(640 * dpiX / 96.f)); UINT nHeight = static_cast(ceil(480 * dpiY / 96.f)); diff --git a/core/Custom/CustomTextRenderer.cpp b/core/Custom/CustomTextRenderer.cpp new file mode 100644 index 00000000..edf73631 --- /dev/null +++ b/core/Custom/CustomTextRenderer.cpp @@ -0,0 +1,346 @@ +#include "..\e2dcustom.h" + +using namespace e2d; + +CustomTextRenderer::CustomTextRenderer( + ID2D1Factory* pD2DFactory, + ID2D1HwndRenderTarget* pRT, + ID2D1SolidColorBrush* pBrush +) + : cRefCount_(0) + , pD2DFactory_(pD2DFactory) + , pRT_(pRT) + , pBrush_(pBrush) + , sFillColor_() + , sOutlineColor_() + , fStrokeWidth_(1) + , fOpacity_(1) +{ + pD2DFactory_->AddRef(); + pRT_->AddRef(); + pBrush_->AddRef(); +} + +CustomTextRenderer::~CustomTextRenderer() +{ + SafeReleaseInterface(&pD2DFactory_); + SafeReleaseInterface(&pRT_); + SafeReleaseInterface(&pBrush_); +} + +STDMETHODIMP_(void) CustomTextRenderer::SetTextStyle( + CONST D2D1_COLOR_F &fillColor, + CONST D2D1_COLOR_F &outlineColor, + FLOAT strokeWidth, + FLOAT opacity +) +{ + sFillColor_ = fillColor; + sOutlineColor_ = outlineColor; + fStrokeWidth_ = strokeWidth; + fOpacity_ = opacity; +} + +STDMETHODIMP CustomTextRenderer::DrawGlyphRun( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + __in DWRITE_GLYPH_RUN const* glyphRun, + __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, + IUnknown* clientDrawingEffect +) +{ + HRESULT hr = S_OK; + + ID2D1PathGeometry* pPathGeometry = NULL; + hr = pD2DFactory_->CreatePathGeometry( + &pPathGeometry + ); + + ID2D1GeometrySink* pSink = NULL; + if (SUCCEEDED(hr)) + { + hr = pPathGeometry->Open( + &pSink + ); + } + + if (SUCCEEDED(hr)) + { + hr = glyphRun->fontFace->GetGlyphRunOutline( + glyphRun->fontEmSize, + glyphRun->glyphIndices, + glyphRun->glyphAdvances, + glyphRun->glyphOffsets, + glyphRun->glyphCount, + glyphRun->isSideways, + glyphRun->bidiLevel % 2, + pSink + ); + } + + if (SUCCEEDED(hr)) + { + hr = pSink->Close(); + } + + D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F( + 1.0f, 0.0f, + 0.0f, 1.0f, + baselineOriginX, baselineOriginY + ); + + ID2D1TransformedGeometry* pTransformedGeometry = NULL; + if (SUCCEEDED(hr)) + { + hr = pD2DFactory_->CreateTransformedGeometry( + pPathGeometry, + &matrix, + &pTransformedGeometry + ); + } + + if (SUCCEEDED(hr)) + { + pBrush_->SetOpacity(fOpacity_); + pBrush_->SetColor(sOutlineColor_); + + pRT_->DrawGeometry( + pTransformedGeometry, + pBrush_, + fStrokeWidth_ + ); + + pBrush_->SetColor(sFillColor_); + + pRT_->FillGeometry( + pTransformedGeometry, + pBrush_ + ); + } + + SafeReleaseInterface(&pPathGeometry); + SafeReleaseInterface(&pSink); + SafeReleaseInterface(&pTransformedGeometry); + + return hr; +} + +STDMETHODIMP CustomTextRenderer::DrawUnderline( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + __in DWRITE_UNDERLINE const* underline, + IUnknown* clientDrawingEffect +) +{ + HRESULT hr; + + D2D1_RECT_F rect = D2D1::RectF( + 0, + underline->offset, + underline->width, + underline->offset + underline->thickness + ); + + ID2D1RectangleGeometry* pRectangleGeometry = NULL; + hr = pD2DFactory_->CreateRectangleGeometry( + &rect, + &pRectangleGeometry + ); + + D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F( + 1.0f, 0.0f, + 0.0f, 1.0f, + baselineOriginX, baselineOriginY + ); + + ID2D1TransformedGeometry* pTransformedGeometry = NULL; + if (SUCCEEDED(hr)) + { + hr = pD2DFactory_->CreateTransformedGeometry( + pRectangleGeometry, + &matrix, + &pTransformedGeometry + ); + } + + if (SUCCEEDED(hr)) + { + pBrush_->SetOpacity(fOpacity_); + pBrush_->SetColor(sOutlineColor_); + + pRT_->DrawGeometry( + pTransformedGeometry, + pBrush_, + fStrokeWidth_ + ); + + pBrush_->SetColor(sFillColor_); + + pRT_->FillGeometry( + pTransformedGeometry, + pBrush_ + ); + } + + SafeReleaseInterface(&pRectangleGeometry); + SafeReleaseInterface(&pTransformedGeometry); + + return S_OK; +} + +STDMETHODIMP CustomTextRenderer::DrawStrikethrough( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + __in DWRITE_STRIKETHROUGH const* strikethrough, + IUnknown* clientDrawingEffect +) +{ + HRESULT hr; + + D2D1_RECT_F rect = D2D1::RectF( + 0, + strikethrough->offset, + strikethrough->width, + strikethrough->offset + strikethrough->thickness + ); + + ID2D1RectangleGeometry* pRectangleGeometry = NULL; + hr = pD2DFactory_->CreateRectangleGeometry( + &rect, + &pRectangleGeometry + ); + + D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F( + 1.0f, 0.0f, + 0.0f, 1.0f, + baselineOriginX, baselineOriginY + ); + + ID2D1TransformedGeometry* pTransformedGeometry = NULL; + if (SUCCEEDED(hr)) + { + hr = pD2DFactory_->CreateTransformedGeometry( + pRectangleGeometry, + &matrix, + &pTransformedGeometry + ); + } + + if (SUCCEEDED(hr)) + { + pBrush_->SetOpacity(fOpacity_); + pBrush_->SetColor(sOutlineColor_); + + pRT_->DrawGeometry( + pTransformedGeometry, + pBrush_, + fStrokeWidth_ + ); + + pBrush_->SetColor(sFillColor_); + + pRT_->FillGeometry( + pTransformedGeometry, + pBrush_ + ); + } + + SafeReleaseInterface(&pRectangleGeometry); + SafeReleaseInterface(&pTransformedGeometry); + + return S_OK; +} + +STDMETHODIMP CustomTextRenderer::DrawInlineObject( + __maybenull void* clientDrawingContext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject* inlineObject, + BOOL isSideways, + BOOL isRightToLeft, + IUnknown* clientDrawingEffect +) +{ + return E_NOTIMPL; +} + +STDMETHODIMP_(unsigned long) CustomTextRenderer::AddRef() +{ + return InterlockedIncrement(&cRefCount_); +} + +STDMETHODIMP_(unsigned long) CustomTextRenderer::Release() +{ + unsigned long newCount = InterlockedDecrement(&cRefCount_); + + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; +} + +STDMETHODIMP CustomTextRenderer::IsPixelSnappingDisabled( + __maybenull void* clientDrawingContext, + __out BOOL* isDisabled +) +{ + *isDisabled = FALSE; + return S_OK; +} + +STDMETHODIMP CustomTextRenderer::GetCurrentTransform( + __maybenull void* clientDrawingContext, + __out DWRITE_MATRIX* transform +) +{ + pRT_->GetTransform(reinterpret_cast(transform)); + return S_OK; +} + +STDMETHODIMP CustomTextRenderer::GetPixelsPerDip( + __maybenull void* clientDrawingContext, + __out FLOAT* pixelsPerDip +) +{ + float x, yUnused; + + pRT_->GetDpi(&x, &yUnused); + *pixelsPerDip = x / 96; + + return S_OK; +} + +STDMETHODIMP CustomTextRenderer::QueryInterface( + IID const& riid, + void** ppvObject +) +{ + if (__uuidof(IDWriteTextRenderer) == riid) + { + *ppvObject = this; + } + else if (__uuidof(IDWritePixelSnapping) == riid) + { + *ppvObject = this; + } + else if (__uuidof(IUnknown) == riid) + { + *ppvObject = this; + } + else + { + *ppvObject = NULL; + return E_FAIL; + } + + AddRef(); + + return S_OK; +} \ No newline at end of file diff --git a/core/e2dbase.h b/core/e2dbase.h index 04efb98b..e69f2421 100644 --- a/core/e2dbase.h +++ b/core/e2dbase.h @@ -241,6 +241,12 @@ public: bool show = true ); + // 获取系统 DPI 缩放 + static float getDpiScaleX(); + + // 获取系统 DPI 缩放 + static float getDpiScaleY(); + // 获取 ID2D1Factory 对象 static ID2D1Factory * getID2D1Factory(); @@ -256,6 +262,9 @@ public: // 获取 IDWriteFactory 对象 static IDWriteFactory * getIDWriteFactory(); + // 获取自定义的文字渲染器 + static CustomTextRenderer * getCustomTextRenderer(); + private: // 渲染游戏画面 static void __render(); diff --git a/core/e2dcommon.h b/core/e2dcommon.h index 9bf4f01e..a48a3e04 100644 --- a/core/e2dcommon.h +++ b/core/e2dcommon.h @@ -1,5 +1,6 @@ #pragma once #include "e2dmacros.h" +#include "e2dcustom.h" #include #include #include @@ -677,13 +678,7 @@ protected: }; -template -inline void SafeDelete(T** p) { if (*p) { delete *p; *p = nullptr; } } - template inline void SafeRelease(Object** p) { if (*p) { (*p)->release(); *p = nullptr; } } -template -inline void SafeReleaseInterface(Interface **pp) { if (*pp != nullptr) { (*pp)->Release(); (*pp) = nullptr; } } - } \ No newline at end of file diff --git a/core/e2dcustom.h b/core/e2dcustom.h new file mode 100644 index 00000000..d50aec73 --- /dev/null +++ b/core/e2dcustom.h @@ -0,0 +1,106 @@ +#pragma once +#include "e2dmacros.h" + +namespace e2d +{ + template + inline void SafeReleaseInterface(Interface **pp) + { + if (*pp != nullptr) + { + (*pp)->Release(); + (*pp) = nullptr; + } + } + + // 自定义的文字渲染器 + class CustomTextRenderer + : public IDWriteTextRenderer + { + public: + CustomTextRenderer( + ID2D1Factory* pD2DFactory, + ID2D1HwndRenderTarget* pRT, + ID2D1SolidColorBrush* pBrush + ); + + ~CustomTextRenderer(); + + STDMETHOD_(void, SetTextStyle)( + CONST D2D1_COLOR_F &fillColor, + CONST D2D1_COLOR_F &outlineColor, + FLOAT strokeWidth, + FLOAT opacity + ); + + STDMETHOD(DrawGlyphRun)( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + __in DWRITE_GLYPH_RUN const* glyphRun, + __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, + IUnknown* clientDrawingEffect + ); + + STDMETHOD(DrawUnderline)( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + __in DWRITE_UNDERLINE const* underline, + IUnknown* clientDrawingEffect + ); + + STDMETHOD(DrawStrikethrough)( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + __in DWRITE_STRIKETHROUGH const* strikethrough, + IUnknown* clientDrawingEffect + ); + + STDMETHOD(DrawInlineObject)( + __maybenull void* clientDrawingContext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject* inlineObject, + BOOL isSideways, + BOOL isRightToLeft, + IUnknown* clientDrawingEffect + ); + + STDMETHOD(IsPixelSnappingDisabled)( + __maybenull void* clientDrawingContext, + __out BOOL* isDisabled + ); + + STDMETHOD(GetCurrentTransform)( + __maybenull void* clientDrawingContext, + __out DWRITE_MATRIX* transform + ); + + STDMETHOD(GetPixelsPerDip)( + __maybenull void* clientDrawingContext, + __out FLOAT* pixelsPerDip + ); + + public: + unsigned long STDMETHODCALLTYPE AddRef(); + unsigned long STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface( + IID const& riid, + void** ppvObject + ); + + private: + unsigned long cRefCount_; + D2D1_COLOR_F sFillColor_; + D2D1_COLOR_F sOutlineColor_; + FLOAT fStrokeWidth_; + FLOAT fOpacity_; + ID2D1Factory* pD2DFactory_; + ID2D1HwndRenderTarget* pRT_; + ID2D1SolidColorBrush* pBrush_; + }; + +} \ No newline at end of file diff --git a/project/vs2017/Easy2D.vcxproj b/project/vs2017/Easy2D.vcxproj index 8e4c9bdc..6befa023 100644 --- a/project/vs2017/Easy2D.vcxproj +++ b/project/vs2017/Easy2D.vcxproj @@ -222,6 +222,7 @@ + @@ -255,6 +256,7 @@ + diff --git a/project/vs2017/Easy2D.vcxproj.filters b/project/vs2017/Easy2D.vcxproj.filters index 3e6e7963..3be5a9e0 100644 --- a/project/vs2017/Easy2D.vcxproj.filters +++ b/project/vs2017/Easy2D.vcxproj.filters @@ -31,6 +31,9 @@ {d3968182-2399-40d4-8e39-accb4711018c} + + {3475b59d-d50c-43b1-8334-bcb9e1703ed2} + @@ -219,6 +222,9 @@ Tool\Listener + + Custom + @@ -232,5 +238,6 @@ + \ No newline at end of file