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,