diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj index 6bfacbde..a3910d00 100644 --- a/projects/kiwano/kiwano.vcxproj +++ b/projects/kiwano/kiwano.vcxproj @@ -88,6 +88,7 @@ + @@ -183,6 +184,7 @@ + diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters index a915dc60..8853f034 100644 --- a/projects/kiwano/kiwano.vcxproj.filters +++ b/projects/kiwano/kiwano.vcxproj.filters @@ -408,6 +408,9 @@ math + + render\DirectX + @@ -671,6 +674,9 @@ event\listener + + render\DirectX + diff --git a/src/kiwano/render/DirectX/D2DDeviceResources.cpp b/src/kiwano/render/DirectX/D2DDeviceResources.cpp index 368df4b5..e061349c 100644 --- a/src/kiwano/render/DirectX/D2DDeviceResources.cpp +++ b/src/kiwano/render/DirectX/D2DDeviceResources.cpp @@ -19,6 +19,7 @@ // THE SOFTWARE. #include +#include #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "dwrite.lib") @@ -522,6 +523,18 @@ HRESULT D2DDeviceResources::CreateTextLayout(_Out_ ComPtr& te HRESULT hr = dwrite_factory_->CreateTextLayout(text, length, text_format.Get(), 0, 0, &output); + if (SUCCEEDED(hr)) + { + ComPtr effect; + hr = ITextDrawingEffect::Create(&effect, factory_.Get()); + + if (SUCCEEDED(hr)) + { + DWRITE_TEXT_RANGE textRange = { 0, std::numeric_limits::max() }; + hr = output->SetDrawingEffect(effect.Get(), textRange); + } + } + if (SUCCEEDED(hr)) { text_layout = output; diff --git a/src/kiwano/render/DirectX/FontCollectionLoader.h b/src/kiwano/render/DirectX/FontCollectionLoader.h index 587c7e00..bd23c445 100644 --- a/src/kiwano/render/DirectX/FontCollectionLoader.h +++ b/src/kiwano/render/DirectX/FontCollectionLoader.h @@ -29,7 +29,7 @@ interface DWRITE_DECLARE_INTERFACE("7EC7A55A-1964-4098-83E0-EFA7C12C6EF7") IFont : public IDWriteFontCollectionLoader { public: - static HRESULT Create(_Out_ IFontCollectionLoader * *ppCollectionLoader); + static HRESULT Create(_Out_ IFontCollectionLoader** ppCollectionLoader); STDMETHOD(AddFilePaths) (const Vector& filePaths, _Out_ LPVOID* pCollectionKey, _Out_ uint32_t* pCollectionKeySize) PURE; @@ -39,7 +39,7 @@ interface DWRITE_DECLARE_INTERFACE("0A1A3F2A-85F2-41BB-80FD-EC01271740C4") IFont : public IDWriteFontFileEnumerator { public: - static HRESULT Create(_Out_ IFontFileEnumerator * *ppEnumerator, IDWriteFactory * pFactory); + static HRESULT Create(_Out_ IFontFileEnumerator** ppEnumerator, IDWriteFactory* pFactory); STDMETHOD(SetFilePaths)(const Vector& filePaths) PURE; }; @@ -48,8 +48,7 @@ interface DWRITE_DECLARE_INTERFACE("F2C411F0-2FB0-4D0E-8C73-D2B8F30137A4") IReso : public IDWriteFontCollectionLoader { public: - static HRESULT Create(_Out_ IResourceFontCollectionLoader * *ppCollectionLoader, - IDWriteFontFileLoader * pFileLoader); + static HRESULT Create(_Out_ IResourceFontCollectionLoader** ppCollectionLoader, IDWriteFontFileLoader* pFileLoader); STDMETHOD(AddResources) (const Vector& data, _Out_ LPVOID* pCollectionKey, _Out_ uint32_t* pCollectionKeySize) PURE; @@ -59,15 +58,15 @@ interface DWRITE_DECLARE_INTERFACE("08D21408-6FC1-4E36-A4EB-4DA16BE3399E") IReso : public IDWriteFontFileLoader { public: - static HRESULT Create(_Out_ IResourceFontFileLoader * *ppFileLoader); + static HRESULT Create(_Out_ IResourceFontFileLoader** ppFileLoader); }; interface DWRITE_DECLARE_INTERFACE("0AD0EC74-7503-46E8-8899-520175ECCB4A") IResourceFontFileEnumerator : public IDWriteFontFileEnumerator { public: - static HRESULT Create(_Out_ IResourceFontFileEnumerator * *ppEnumerator, IDWriteFactory * pFactory, - IDWriteFontFileLoader * pFileLoader); + static HRESULT Create(_Out_ IResourceFontFileEnumerator** ppEnumerator, IDWriteFactory* pFactory, + IDWriteFontFileLoader* pFileLoader); STDMETHOD(SetResources)(const Vector& data) PURE; }; @@ -76,7 +75,7 @@ interface DWRITE_DECLARE_INTERFACE("A6267450-27F3-4948-995F-FF8345A72F88") IReso : public IDWriteFontFileStream { public: - static HRESULT Create(_Out_ IResourceFontFileStream * *ppStream, const BinaryData& data); + static HRESULT Create(_Out_ IResourceFontFileStream** ppStream, const BinaryData& data); }; } // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.cpp b/src/kiwano/render/DirectX/TextDrawingEffect.cpp new file mode 100644 index 00000000..278d47a7 --- /dev/null +++ b/src/kiwano/render/DirectX/TextDrawingEffect.cpp @@ -0,0 +1,197 @@ +// Copyright (c) 2023 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include + +namespace kiwano +{ +class TextDrawingEffect : public ITextDrawingEffect +{ +public: + TextDrawingEffect(); + + STDMETHOD(CreateDeviceResources)(_In_ ID2D1Factory* pFactory); + + STDMETHOD(CreateOutlineGeomerty) + (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY); + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + +private: + unsigned long cRefCount_; + ComPtr pFactory_; + + // Outline geometry cache + const DWRITE_GLYPH_RUN* pLastGlyphRun_; + float fLastOriginX_; + float fLastOriginY_; + ComPtr pOutlineGeo_; +}; + +HRESULT ITextDrawingEffect::Create(_Out_ ITextDrawingEffect** ppTextDrawingEffect, _In_ ID2D1Factory* pFactory) +{ + HRESULT hr = E_FAIL; + + if (ppTextDrawingEffect) + { + TextDrawingEffect* pTextDrawingEffect = new (std::nothrow) TextDrawingEffect; + if (pTextDrawingEffect) + { + hr = pTextDrawingEffect->CreateDeviceResources(pFactory); + + if (SUCCEEDED(hr)) + { + pTextDrawingEffect->AddRef(); + + DX::SafeRelease(*ppTextDrawingEffect); + (*ppTextDrawingEffect) = pTextDrawingEffect; + return S_OK; + } + else + { + delete pTextDrawingEffect; + pTextDrawingEffect = NULL; + } + } + } + return hr; +} + +TextDrawingEffect::TextDrawingEffect() + : cRefCount_(0) + , pLastGlyphRun_(nullptr) + , fLastOriginX_(0) + , fLastOriginY_(0) +{ +} + +STDMETHODIMP TextDrawingEffect::CreateDeviceResources(_In_ ID2D1Factory* pFactory) +{ + pFactory_ = pFactory; + return S_OK; +} + +STDMETHODIMP TextDrawingEffect::CreateOutlineGeomerty(_Out_ ID2D1Geometry** ppOutlineGeo, + _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, + float fOriginY) +{ + HRESULT hr = S_OK; + + if (pOutlineGeo_ && glyphRun == pLastGlyphRun_ && fOriginX == fLastOriginX_ && fOriginY == fLastOriginY_) + { + // Use cached geometry + pOutlineGeo_->AddRef(); + DX::SafeRelease(*ppOutlineGeo); + (*ppOutlineGeo) = pOutlineGeo_.Get(); + return S_OK; + } + + ComPtr pSink; + ComPtr pPathGeometry; + ComPtr pTransformedGeometry; + + if (SUCCEEDED(hr)) + { + hr = pFactory_->CreatePathGeometry(&pPathGeometry); + } + + 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.Get()); + } + + if (SUCCEEDED(hr)) + { + hr = pSink->Close(); + } + + if (SUCCEEDED(hr)) + { + D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, fOriginX, fOriginY); + + if (SUCCEEDED(hr)) + { + hr = pFactory_->CreateTransformedGeometry(pPathGeometry.Get(), &matrix, &pTransformedGeometry); + } + } + } + + if (SUCCEEDED(hr)) + { + pOutlineGeo_ = pTransformedGeometry; + pLastGlyphRun_ = glyphRun; + fLastOriginX_ = fOriginX; + fLastOriginY_ = fOriginY; + + pOutlineGeo_->AddRef(); + DX::SafeRelease(*ppOutlineGeo); + (*ppOutlineGeo) = pOutlineGeo_.Get(); + } + return hr; +} + +STDMETHODIMP_(unsigned long) TextDrawingEffect::AddRef() +{ + return InterlockedIncrement(&cRefCount_); +} + +STDMETHODIMP_(unsigned long) TextDrawingEffect::Release() +{ + unsigned long newCount = InterlockedDecrement(&cRefCount_); + + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; +} + +STDMETHODIMP TextDrawingEffect::QueryInterface(REFIID riid, void** ppvObject) +{ + if (__uuidof(ITextDrawingEffect) == riid) + { + *ppvObject = this; + } + else if (__uuidof(IUnknown) == riid) + { + *ppvObject = this; + } + else + { + *ppvObject = NULL; + return E_FAIL; + } + + AddRef(); + + return S_OK; +} +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.h b/src/kiwano/render/DirectX/TextDrawingEffect.h new file mode 100644 index 00000000..81fb1217 --- /dev/null +++ b/src/kiwano/render/DirectX/TextDrawingEffect.h @@ -0,0 +1,36 @@ +// Copyright (c) 2023 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include +#include + +namespace kiwano +{ + +interface DWRITE_DECLARE_INTERFACE("7431F439-6E54-4707-A0DC-1AA035D6AFB8") ITextDrawingEffect : public IUnknown +{ +public: + static HRESULT Create(_Out_ ITextDrawingEffect** ppTextDrawingEffect, _In_ ID2D1Factory* pFactory); + + STDMETHOD(CreateOutlineGeomerty) + (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY) PURE; +}; +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextRenderer.cpp b/src/kiwano/render/DirectX/TextRenderer.cpp index 0f90e265..8329f965 100644 --- a/src/kiwano/render/DirectX/TextRenderer.cpp +++ b/src/kiwano/render/DirectX/TextRenderer.cpp @@ -19,6 +19,7 @@ // THE SOFTWARE. #include +#include namespace kiwano { @@ -110,9 +111,6 @@ TextRenderer::TextRenderer() , cPrimitivesCount_(0) , fDefaultOutlineWidth_(1) { - if (pRT_) - { - } } TextRenderer::~TextRenderer() {} @@ -162,71 +160,34 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, KGE_NOT_USED(measuringMode); KGE_NOT_USED(glyphRunDescription); - HRESULT hr = S_OK; + ComPtr pTextDrawingEffect; + + HRESULT hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); if (pDefaultOutlineBrush_) { - ComPtr pSink; - ComPtr pPathGeometry; - ComPtr pTransformedGeometry; + ComPtr pOutlineGeometry; if (SUCCEEDED(hr)) { - hr = pFactory_->CreatePathGeometry(&pPathGeometry); + hr = pTextDrawingEffect->CreateOutlineGeomerty(&pOutlineGeometry, glyphRun, baselineOriginX, + baselineOriginY); } if (SUCCEEDED(hr)) { - hr = pPathGeometry->Open(&pSink); + pRT_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, + pDefaultStrokeStyle_.Get()); - if (SUCCEEDED(hr)) - { - hr = glyphRun->fontFace->GetGlyphRunOutline( - glyphRun->fontEmSize, glyphRun->glyphIndices, glyphRun->glyphAdvances, glyphRun->glyphOffsets, - glyphRun->glyphCount, glyphRun->isSideways, glyphRun->bidiLevel % 2, pSink.Get()); - } - - if (SUCCEEDED(hr)) - { - hr = pSink->Close(); - } - - if (SUCCEEDED(hr)) - { - D2D1::Matrix3x2F const matrix = - D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY); - - if (SUCCEEDED(hr)) - { - hr = pFactory_->CreateTransformedGeometry(pPathGeometry.Get(), &matrix, &pTransformedGeometry); - } - - if (SUCCEEDED(hr)) - { - pRT_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; - } - } + ++cPrimitivesCount_; } } - if (SUCCEEDED(hr)) + if (pDefaultFillBrush_) { - ComPtr pCurrentFillBrush; - if (clientDrawingEffect) + if (SUCCEEDED(hr)) { - hr = clientDrawingEffect->QueryInterface(&pCurrentFillBrush); - } - else - { - pCurrentFillBrush = pDefaultFillBrush_; - } - - if (SUCCEEDED(hr) && pCurrentFillBrush) - { - pRT_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, pCurrentFillBrush.Get()); + pRT_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, pDefaultFillBrush_.Get()); ++cPrimitivesCount_; } diff --git a/src/kiwano/render/DirectX/TextRenderer.h b/src/kiwano/render/DirectX/TextRenderer.h index 63234daf..15c47347 100644 --- a/src/kiwano/render/DirectX/TextRenderer.h +++ b/src/kiwano/render/DirectX/TextRenderer.h @@ -26,7 +26,7 @@ namespace kiwano interface DWRITE_DECLARE_INTERFACE("b293e798-9916-4096-a3c1-e5d4039dfa64") ITextRenderer : public IDWriteTextRenderer { public: - static KGE_API HRESULT Create(_Out_ ITextRenderer * *ppTextRenderer, _In_ ID2D1RenderTarget * pRT); + static KGE_API HRESULT Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1RenderTarget* pRT); STDMETHOD(DrawTextLayout) (_In_ IDWriteTextLayout * pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pDefaultFillBrush,