diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-03-11 15:34:41 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2021-03-17 22:48:45 +0100 |
commit | 3f69ec9ab4236de13d229f675943123aeb42ea29 (patch) | |
tree | d970f0046bb4a5f55df4aa238d5d1bf68e066483 /sw | |
parent | a001a66ba27e2fe9a485388869d53f001f2b09af (diff) |
improve font caching in SwFntObj
The code previously cached only SalGlyphItems, but such caching
fails if SalLayout::GetGlyphs() does not return valid glyphs,
which is the case with MultiSalLayout. Worse, it not only fails,
but layout is once computed for caching, that fails, and is computed
a second time for actual use.
This improved cache caches the result of GetTextWidth(), which
itself improves the performance a bit, but it also allows caching
the value for GetGlyph() not returning valid glyphs. Moreover this
also caches failures to get valid glyphs from GetGlyphs().
There are quite some calls to GetTextArray(), I didn't cache those,
but it might be added if needed.
Change-Id: Ia2589fb1b778f4f154c88f65d9906584284239da
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112588
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
Diffstat (limited to 'sw')
-rw-r--r-- | sw/source/core/inc/fntcache.hxx | 23 | ||||
-rw-r--r-- | sw/source/core/txtnode/fntcache.cxx | 136 |
2 files changed, 103 insertions, 56 deletions
diff --git a/sw/source/core/inc/fntcache.hxx b/sw/source/core/inc/fntcache.hxx index 61564de93e72..848ad3611c10 100644 --- a/sw/source/core/inc/fntcache.hxx +++ b/sw/source/core/inc/fntcache.hxx @@ -60,19 +60,9 @@ void SwClearFntCacheTextGlyphs(); extern SwFntCache *pFntCache; extern SwFntObj *pLastFont; -/** - * Defines a substring on a given output device, to be used as an std::map<> - * key. - */ -struct SwTextGlyphsKey -{ - VclPtr<OutputDevice> m_pOutputDevice; - OUString m_aText; - sal_Int32 m_nIndex; - sal_Int32 m_nLength; - -}; +struct SwTextGlyphsKey; bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r); +struct SwTextGlyphsData; class SwFntObj : public SwCacheObj { @@ -95,8 +85,8 @@ class SwFntObj : public SwCacheObj bool m_bSymbol : 1; bool m_bPaintBlank : 1; - /// Cache of already calculated layout glyphs. - std::map<SwTextGlyphsKey, SalLayoutGlyphs> m_aTextGlyphs; + /// Cache of already calculated layout glyphs and text widths. + std::map<SwTextGlyphsKey, SwTextGlyphsData> m_aTextGlyphs; static tools::Long nPixWidth; static MapMode *pPixMap; @@ -125,7 +115,10 @@ public: sal_uInt16 GetZoom() const { return m_nZoom; } sal_uInt16 GetPropWidth() const { return m_nPropWidth; } bool IsSymbol() const { return m_bSymbol; } - std::map<SwTextGlyphsKey, SalLayoutGlyphs>& GetTextGlyphs() { return m_aTextGlyphs; } + + tools::Long GetCachedTextWidth(const SwTextGlyphsKey& key, const vcl::TextLayoutCache* vclCache); + SalLayoutGlyphs* GetCachedSalLayoutGlyphs(const SwTextGlyphsKey& key); + void ClearCachedTextGlyphs(); void DrawText( SwDrawTextInfo &rInf ); /// determine the TextSize (of the printer) diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx index 5b8661b4cf5a..b0ed8eb4d72b 100644 --- a/sw/source/core/txtnode/fntcache.cxx +++ b/sw/source/core/txtnode/fntcache.cxx @@ -71,6 +71,28 @@ tools::Long SwFntObj::nPixWidth; MapMode* SwFntObj::pPixMap = nullptr; static vcl::DeleteOnDeinit< VclPtr<OutputDevice> > s_pFntObjPixOut( new VclPtr<OutputDevice> ); +/** + * Defines a substring on a given output device, to be used as an std::map<> + * key. + */ +struct SwTextGlyphsKey +{ + VclPtr<OutputDevice> m_pOutputDevice; + OUString m_aText; + sal_Int32 m_nIndex; + sal_Int32 m_nLength; + +}; + +/** + * Glyphs and text width for the given SwTextGlyphsKey. + */ +struct SwTextGlyphsData +{ + SalLayoutGlyphs m_aTextGlyphs; + tools::Long m_nTextWidth = -1; // -1 = not computed yet +}; + namespace { @@ -93,36 +115,6 @@ tools::Long EvalGridWidthAdd( const SwTextGridItem *const pGrid, const SwDrawTex return nGridWidthAdd; } -/** - * Pre-calculates glyph items for the rendered subset of rKey's text, assuming - * outdev state does not change between the outdev calls. - */ -SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, SalLayoutGlyphs& rTextGlyphs) -{ - // Use pre-calculated result. - if (rTextGlyphs.IsValid()) - return &rTextGlyphs; - - if (rKey.m_nIndex >= rKey.m_aText.getLength()) - // Same as in OutputDevice::GetTextArray(). - return nullptr; - - // Calculate glyph items. - std::unique_ptr<SalLayout> pLayout - = rKey.m_pOutputDevice->ImplLayout(rKey.m_aText, rKey.m_nIndex, rKey.m_nLength, Point(0, 0), 0, - nullptr, SalLayoutFlags::GlyphItemsOnly); - if (!pLayout) - return nullptr; - - const SalLayoutGlyphs* pGlyphs = pLayout->GetGlyphs(); - if (!pGlyphs) - return nullptr; - - // Remember the calculation result. - rTextGlyphs = *pGlyphs; - - return &rTextGlyphs; -} } bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r) @@ -223,6 +215,72 @@ void SwFntObj::CreatePrtFont( const OutputDevice& rPrt ) } +/** + * Pre-calculates glyph items for the rendered subset of rKey's text, assuming + * outdev state does not change between the outdev calls. + */ +static SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it) +{ + assert (!it->second.m_aTextGlyphs.IsValid()); + + if (rKey.m_nIndex >= rKey.m_aText.getLength()) + // Same as in OutputDevice::GetTextArray(). + return nullptr; + + // Calculate glyph items. + std::unique_ptr<SalLayout> pLayout + = rKey.m_pOutputDevice->ImplLayout(rKey.m_aText, rKey.m_nIndex, rKey.m_nLength, Point(0, 0), 0, + nullptr, SalLayoutFlags::GlyphItemsOnly); + if (!pLayout) + return nullptr; + + const SalLayoutGlyphs* pGlyphs = pLayout->GetGlyphs(); + if (!pGlyphs) + return nullptr; + + // Remember the calculation result. + it->second.m_aTextGlyphs = *pGlyphs; + + return &it->second.m_aTextGlyphs; +} + +SalLayoutGlyphs* SwFntObj::GetCachedSalLayoutGlyphs(const SwTextGlyphsKey& key) +{ + std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it = m_aTextGlyphs.find(key); + if(it != m_aTextGlyphs.end()) + { + if( it->second.m_aTextGlyphs.IsValid()) + return &it->second.m_aTextGlyphs; + // Do not try to create the layout here. If a cache item exists, it's already + // been attempted and the layout was invalid (this happens with MultiSalLayout). + // So in that case this is a cached failure. + return nullptr; + } + it = m_aTextGlyphs.insert_or_assign( it, key, SwTextGlyphsData()); + return lcl_CreateLayout(key, it); +} + +tools::Long SwFntObj::GetCachedTextWidth(const SwTextGlyphsKey& key, const vcl::TextLayoutCache* vclCache) +{ + std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it = m_aTextGlyphs.find(key); + if(it != m_aTextGlyphs.end() && it->second.m_nTextWidth >= 0) + return it->second.m_nTextWidth; + if(it == m_aTextGlyphs.end()) + { + it = m_aTextGlyphs.insert_or_assign( it, key, SwTextGlyphsData()); + lcl_CreateLayout(key, it); + } + it->second.m_nTextWidth = key.m_pOutputDevice->GetTextWidth(key.m_aText, key.m_nIndex, key.m_nLength, vclCache, + it->second.m_aTextGlyphs.IsValid() ? &it->second.m_aTextGlyphs : nullptr ); + assert(it->second.m_nTextWidth >= 0); + return it->second.m_nTextWidth; +} + +void SwFntObj::ClearCachedTextGlyphs() +{ + m_aTextGlyphs.clear(); +} + /* * returns whether we have to adjust the output font to resemble * the formatting font @@ -1472,7 +1530,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) // get screen array std::unique_ptr<tools::Long[]> pScrArray(new tools::Long[sal_Int32(rInf.GetLen())]); SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; - SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); @@ -1487,7 +1545,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) m_pPrinter->SetFont( *m_pPrtFont ); } aGlyphsKey = SwTextGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; - pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); } @@ -1826,7 +1884,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) ? (rInf.GetIdx() ? 1 : 0) : sal_Int32(rInf.GetIdx()); aGlyphsKey = SwTextGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen }; - pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(), nTmpIdx , nLen, SalLayoutFlags::NONE, pGlyphs ); if (bBullet) @@ -2061,10 +2119,7 @@ Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf ) else { SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(nLn) }; - SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); - aTextSize.setWidth( rInf.GetOut().GetTextWidth( rInf.GetText(), - sal_Int32(rInf.GetIdx()), sal_Int32(nLn), - rInf.GetVclCache(), pGlyphs) ); + aTextSize.setWidth( GetCachedTextWidth(aGlyphsKey, rInf.GetVclCache())); rInf.SetKanaDiff( 0 ); } @@ -2098,7 +2153,7 @@ TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf) m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() ); m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() ); SwTextGlyphsKey aGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; - SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); } @@ -2514,8 +2569,7 @@ TextFrameIndex SwFont::GetTextBreak(SwDrawTextInfo const & rInf, tools::Long nTe SwFntAccess aFntAccess(m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex, &m_aSub[m_nActual], rInf.GetShell()); SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), *pTmpText, sal_Int32(nTmpIdx), sal_Int32(nTmpLen) }; - SalLayoutGlyphs* pGlyphs - = lcl_CreateLayout(aGlyphsKey, aFntAccess.Get()->GetTextGlyphs()[aGlyphsKey]); + SalLayoutGlyphs* pGlyphs = aFntAccess.Get()->GetCachedSalLayoutGlyphs(aGlyphsKey); nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak( *pTmpText, nTextWidth, sal_Int32(nTmpIdx), sal_Int32(nTmpLen), @@ -2713,7 +2767,7 @@ bool SwDrawTextInfo::ApplyAutoColor( vcl::Font* pFont ) void SwClearFntCacheTextGlyphs() { for (SwFntObj* pFntObj = pFntCache->First(); pFntObj; pFntObj = SwFntCache::Next(pFntObj)) - pFntObj->GetTextGlyphs().clear(); + pFntObj->ClearCachedTextGlyphs(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |