pref: separate text outline rendering

This commit is contained in:
Nomango 2023-09-24 16:44:26 +08:00
parent 494a4d05f9
commit a7c4e9402b
3 changed files with 205 additions and 95 deletions

View File

@ -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<ID2D1Factory> pFactory_;
// Outline geometry cache
Map<std::tuple<const DWRITE_GLYPH_RUN*, float, float>, ComPtr<ID2D1Geometry>> outlineCache_;
Map<std::tuple<const DWRITE_GLYPH_RUN*, float, float>, ComPtr<ID2D1Geometry>> outlineCache_;
Map<std::tuple<const DWRITE_STRIKETHROUGH*, float, float>, ComPtr<ID2D1Geometry>> strikethroughCache_;
Map<std::tuple<const DWRITE_UNDERLINE*, float, float>, ComPtr<ID2D1Geometry>> 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<ID2D1Geometry> pStrikethroughGeo;
if (SUCCEEDED(hr))
{
ComPtr<ID2D1RectangleGeometry> 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<ID2D1TransformedGeometry> 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<ID2D1Geometry> pUnderlineGeo;
if (SUCCEEDED(hr))
{
ComPtr<ID2D1RectangleGeometry> 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<ID2D1TransformedGeometry> 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_);

View File

@ -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

View File

@ -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<ITextDrawingEffect> pTextDrawingEffect;
HRESULT hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect);
if (pDefaultOutlineBrush_)
HRESULT hr = S_OK;
if (!bOutlineRendering_)
{
ComPtr<ID2D1Geometry> 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<ITextDrawingEffect> pTextDrawingEffect;
hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect);
++cPrimitivesCount_;
ComPtr<ID2D1Geometry> 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<ID2D1RectangleGeometry> pRectangleGeometry;
ComPtr<ID2D1TransformedGeometry> pTransformedGeometry;
ComPtr<ID2D1Brush> pCurrentFillBrush;
ComPtr<ITextDrawingEffect> pTextDrawingEffect;
hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect);
if (clientDrawingEffect)
ComPtr<ID2D1Geometry> pUnderlineGeometry;
if (SUCCEEDED(hr))
{
hr = clientDrawingEffect->QueryInterface<ID2D1Brush>(&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<ID2D1RectangleGeometry> pRectangleGeometry;
ComPtr<ID2D1TransformedGeometry> pTransformedGeometry;
ComPtr<ID2D1Brush> pCurrentFillBrush;
ComPtr<ITextDrawingEffect> pTextDrawingEffect;
hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect);
if (clientDrawingEffect)
ComPtr<ID2D1Geometry> pStrikethroughGeometry;
if (SUCCEEDED(hr))
{
hr = clientDrawingEffect->QueryInterface<ID2D1Brush>(&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;
}