diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.cpp b/src/kiwano/render/DirectX/TextDrawingEffect.cpp index 13b4bd48..b74faf3f 100644 --- a/src/kiwano/render/DirectX/TextDrawingEffect.cpp +++ b/src/kiwano/render/DirectX/TextDrawingEffect.cpp @@ -37,6 +37,13 @@ public: STDMETHOD(CreateOutlineGeomerty) (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY); + STDMETHOD(CreateStrikethroughGeomerty) + (_Out_ ID2D1Geometry** ppStrikethroughGeo, _In_ DWRITE_STRIKETHROUGH const* strikethrough, float fOriginX, + float fOriginY); + + STDMETHOD(CreateUnderlineGeomerty) + (_Out_ ID2D1Geometry** ppUnderlineGeo, _In_ DWRITE_UNDERLINE const* underline, float fOriginX, float fOriginY); + // IUnknown methods virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); virtual ULONG STDMETHODCALLTYPE AddRef(); @@ -46,8 +53,9 @@ private: unsigned long cRefCount_; ComPtr pFactory_; - // Outline geometry cache - Map, ComPtr> outlineCache_; + Map, ComPtr> outlineCache_; + Map, ComPtr> strikethroughCache_; + Map, ComPtr> underlineCache_; }; HRESULT ITextDrawingEffect::Create(_Out_ ITextDrawingEffect** ppTextDrawingEffect, _In_ ID2D1Factory* pFactory) @@ -160,6 +168,115 @@ STDMETHODIMP TextDrawingEffect::CreateOutlineGeomerty(_Out_ ID2D1Geometry** return hr; } +STDMETHODIMP TextDrawingEffect::CreateStrikethroughGeomerty(_Out_ ID2D1Geometry** ppStrikethroughGeo, + _In_ DWRITE_STRIKETHROUGH const* strikethrough, + float fOriginX, float fOriginY) +{ + auto cache = strikethroughCache_.find(std::make_tuple(strikethrough, fOriginX, fOriginY)); + if (cache != strikethroughCache_.end()) + { + auto& pStrikethroughGeo = cache->second; + if (pStrikethroughGeo) + { + // Use cached geometry + pStrikethroughGeo->AddRef(); + DX::SafeRelease(*ppStrikethroughGeo); + (*ppStrikethroughGeo) = pStrikethroughGeo.Get(); + return S_OK; + } + } + + HRESULT hr = S_OK; + + ComPtr pStrikethroughGeo; + if (SUCCEEDED(hr)) + { + ComPtr pRectangleGeometry; + + D2D1_RECT_F rect = D2D1::RectF(0, strikethrough->offset, strikethrough->width, + strikethrough->offset + strikethrough->thickness); + hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + + if (SUCCEEDED(hr)) + { + const auto matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, fOriginX, fOriginY); + + ComPtr pTransformedGeometry; + hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + + if (SUCCEEDED(hr)) + { + pStrikethroughGeo = pTransformedGeometry; + } + } + } + + if (SUCCEEDED(hr)) + { + pStrikethroughGeo->AddRef(); + DX::SafeRelease(*ppStrikethroughGeo); + (*ppStrikethroughGeo) = pStrikethroughGeo.Get(); + + strikethroughCache_.insert( + std::make_pair(std::make_tuple(strikethrough, fOriginX, fOriginY), pStrikethroughGeo)); + } + return hr; +} + +STDMETHODIMP TextDrawingEffect::CreateUnderlineGeomerty(_Out_ ID2D1Geometry** ppUnderlineGeo, + _In_ DWRITE_UNDERLINE const* underline, float fOriginX, + float fOriginY) +{ + auto cache = underlineCache_.find(std::make_tuple(underline, fOriginX, fOriginY)); + if (cache != underlineCache_.end()) + { + auto& pUnderlineGeo = cache->second; + if (pUnderlineGeo) + { + // Use cached geometry + pUnderlineGeo->AddRef(); + DX::SafeRelease(*ppUnderlineGeo); + (*ppUnderlineGeo) = pUnderlineGeo.Get(); + return S_OK; + } + } + + HRESULT hr = S_OK; + + ComPtr pUnderlineGeo; + if (SUCCEEDED(hr)) + { + ComPtr pRectangleGeometry; + + D2D1_RECT_F rect = + D2D1::RectF(0, underline->offset, underline->width, underline->offset + underline->thickness); + hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + + if (SUCCEEDED(hr)) + { + const auto matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, fOriginX, fOriginY); + + ComPtr pTransformedGeometry; + hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + + if (SUCCEEDED(hr)) + { + pUnderlineGeo = pTransformedGeometry; + } + } + } + + if (SUCCEEDED(hr)) + { + pUnderlineGeo->AddRef(); + DX::SafeRelease(*ppUnderlineGeo); + (*ppUnderlineGeo) = pUnderlineGeo.Get(); + + underlineCache_.insert(std::make_pair(std::make_tuple(underline, fOriginX, fOriginY), pUnderlineGeo)); + } + return hr; +} + STDMETHODIMP_(unsigned long) TextDrawingEffect::AddRef() { return InterlockedIncrement(&cRefCount_); diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.h b/src/kiwano/render/DirectX/TextDrawingEffect.h index 05f6280c..5d5de754 100644 --- a/src/kiwano/render/DirectX/TextDrawingEffect.h +++ b/src/kiwano/render/DirectX/TextDrawingEffect.h @@ -36,6 +36,13 @@ public: STDMETHOD(CreateOutlineGeomerty) (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY) PURE; + + STDMETHOD(CreateStrikethroughGeomerty) + (_Out_ ID2D1Geometry** ppStrikethroughGeo, _In_ DWRITE_STRIKETHROUGH const* strikethrough, float fOriginX, + float fOriginY) PURE; + + STDMETHOD(CreateUnderlineGeomerty) + (_Out_ ID2D1Geometry** ppUnderlineGeo, _In_ DWRITE_UNDERLINE const* underline, float fOriginX, float fOriginY) PURE; }; } // namespace directx } // namespace graphics diff --git a/src/kiwano/render/DirectX/TextRenderer.cpp b/src/kiwano/render/DirectX/TextRenderer.cpp index 718ef9ed..de2f22f2 100644 --- a/src/kiwano/render/DirectX/TextRenderer.cpp +++ b/src/kiwano/render/DirectX/TextRenderer.cpp @@ -71,6 +71,7 @@ public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); private: + bool bOutlineRendering_; unsigned long cRefCount_; uint32_t cPrimitivesCount_; float fDefaultOutlineWidth_; @@ -111,7 +112,8 @@ HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1De } TextRenderer::TextRenderer() - : cRefCount_(0) + : bOutlineRendering_(false) + , cRefCount_(0) , cPrimitivesCount_(0) , fDefaultOutlineWidth_(1) { @@ -150,8 +152,16 @@ STDMETHODIMP TextRenderer::DrawTextLayout(_In_ IDWriteTextLayout* pTextLayout, f pDefaultOutlineBrush_ = pDefaultOutlineBrush; fDefaultOutlineWidth_ = fDefaultOutlineWidth; pDefaultStrokeStyle_ = pDefaultStrokeStyle; + bOutlineRendering_ = pDefaultOutlineBrush_ != nullptr; - return pTextLayout->Draw(nullptr, this, fOriginX, fOriginY); + HRESULT hr = pTextLayout->Draw(nullptr, this, fOriginX, fOriginY); + if (SUCCEEDED(hr) && bOutlineRendering_) + { + bOutlineRendering_ = false; + + hr = pTextLayout->Draw(nullptr, this, fOriginX, fOriginY); + } + return hr; } STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, float baselineOriginX, @@ -164,37 +174,41 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, KGE_NOT_USED(measuringMode); KGE_NOT_USED(glyphRunDescription); - ComPtr pTextDrawingEffect; - - HRESULT hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - - if (pDefaultOutlineBrush_) + HRESULT hr = S_OK; + if (!bOutlineRendering_) { - ComPtr pOutlineGeometry; - - if (SUCCEEDED(hr)) + if (pDefaultFillBrush_) { - hr = pTextDrawingEffect->CreateOutlineGeomerty(&pOutlineGeometry, glyphRun, baselineOriginX, - baselineOriginY); - } + if (SUCCEEDED(hr)) + { + pContext_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, + pDefaultFillBrush_.Get()); - if (SUCCEEDED(hr)) - { - pContext_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; + ++cPrimitivesCount_; + } } } - - if (pDefaultFillBrush_) + else { - if (SUCCEEDED(hr)) + if (pDefaultOutlineBrush_) { - pContext_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, - pDefaultFillBrush_.Get()); + ComPtr pTextDrawingEffect; + hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - ++cPrimitivesCount_; + ComPtr pOutlineGeometry; + if (SUCCEEDED(hr)) + { + hr = pTextDrawingEffect->CreateOutlineGeomerty(&pOutlineGeometry, glyphRun, baselineOriginX, + baselineOriginY); + } + + if (SUCCEEDED(hr)) + { + pContext_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, + pDefaultStrokeStyle_.Get()); + + ++cPrimitivesCount_; + } } } return hr; @@ -208,51 +222,37 @@ STDMETHODIMP TextRenderer::DrawUnderline(__maybenull void* clientDrawingContext, HRESULT hr = S_OK; - ComPtr pRectangleGeometry; - ComPtr pTransformedGeometry; - ComPtr pCurrentFillBrush; + ComPtr pTextDrawingEffect; + hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - if (clientDrawingEffect) + ComPtr pUnderlineGeometry; + if (SUCCEEDED(hr)) { - hr = clientDrawingEffect->QueryInterface(&pCurrentFillBrush); - } - else - { - pCurrentFillBrush = pDefaultFillBrush_; + hr = pTextDrawingEffect->CreateUnderlineGeomerty(&pUnderlineGeometry, underline, baselineOriginX, + baselineOriginY); } - if (pCurrentFillBrush || pDefaultOutlineBrush_) + if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) + if (!bOutlineRendering_) { - D2D1_RECT_F rect = - D2D1::RectF(0, underline->offset, underline->width, underline->offset + underline->thickness); - - hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + if (pDefaultFillBrush_) + { + pContext_->FillGeometry(pUnderlineGeometry.Get(), pDefaultFillBrush_.Get()); + ++cPrimitivesCount_; + } } - - if (SUCCEEDED(hr)) + else { - D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY); + if (pDefaultOutlineBrush_) + { + pContext_->DrawGeometry(pUnderlineGeometry.Get(), pDefaultOutlineBrush_.Get(), + fDefaultOutlineWidth_, pDefaultStrokeStyle_.Get()); - hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + ++cPrimitivesCount_; + } } } - - if (SUCCEEDED(hr) && pDefaultOutlineBrush_) - { - pContext_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; - } - - if (SUCCEEDED(hr) && pCurrentFillBrush) - { - pContext_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); - - ++cPrimitivesCount_; - } return hr; } @@ -264,51 +264,37 @@ STDMETHODIMP TextRenderer::DrawStrikethrough(__maybenull void* clientDrawingCont HRESULT hr = S_OK; - ComPtr pRectangleGeometry; - ComPtr pTransformedGeometry; - ComPtr pCurrentFillBrush; + ComPtr pTextDrawingEffect; + hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - if (clientDrawingEffect) + ComPtr pStrikethroughGeometry; + if (SUCCEEDED(hr)) { - hr = clientDrawingEffect->QueryInterface(&pCurrentFillBrush); - } - else - { - pCurrentFillBrush = pDefaultFillBrush_; + hr = pTextDrawingEffect->CreateStrikethroughGeomerty(&pStrikethroughGeometry, strikethrough, baselineOriginX, + baselineOriginY); } - if (pCurrentFillBrush || pDefaultOutlineBrush_) + if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) + if (!bOutlineRendering_) { - D2D1_RECT_F rect = D2D1::RectF(0, strikethrough->offset, strikethrough->width, - strikethrough->offset + strikethrough->thickness); - - hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + if (pDefaultFillBrush_) + { + pContext_->FillGeometry(pStrikethroughGeometry.Get(), pDefaultFillBrush_.Get()); + ++cPrimitivesCount_; + } } - - if (SUCCEEDED(hr)) + else { - D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY); + if (pDefaultOutlineBrush_) + { + pContext_->DrawGeometry(pStrikethroughGeometry.Get(), pDefaultOutlineBrush_.Get(), + fDefaultOutlineWidth_, pDefaultStrokeStyle_.Get()); - hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + ++cPrimitivesCount_; + } } } - - if (SUCCEEDED(hr) && pDefaultOutlineBrush_) - { - pContext_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; - } - - if (SUCCEEDED(hr) && pCurrentFillBrush) - { - pContext_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); - - ++cPrimitivesCount_; - } return hr; }