include/vcl/glyphitemcache.hxx | 4 +- vcl/source/gdi/impglyphitem.cxx | 60 ++++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 23 deletions(-)
New commits: commit 2cb75eb504e3fedb9c93bbfee3609021f583f849 Author: Luboš Luňák <l.lu...@collabora.com> AuthorDate: Wed May 4 06:37:08 2022 +0200 Commit: Luboš Luňák <l.lu...@collabora.com> CommitDate: Wed May 4 08:43:21 2022 +0200 lay out entire strings in SalLayoutGlyphsCache more often (tdf#148911) The document has parts of paragraphs e.g. underlined, which changes the vcl::Font part of the hash key. So if a paragraph starts that way but the rest is not underlined, the optimization of laying out the entire string would be missed. So do the optimization if there are subsequent calls for adjacent parts of the string (or starting at the same index and different length). Change-Id: I3d4a78b0eae42bd3085e96024340e6ed8daa9ad1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133790 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lu...@collabora.com> diff --git a/include/vcl/glyphitemcache.hxx b/include/vcl/glyphitemcache.hxx index a17f53c61e56..138adb7bb40a 100644 --- a/include/vcl/glyphitemcache.hxx +++ b/include/vcl/glyphitemcache.hxx @@ -96,8 +96,8 @@ private: // Last temporary glyphs returned (pointer is returned, so the object needs to be kept somewhere). std::optional<CachedGlyphsKey> mLastTemporaryKey; SalLayoutGlyphs mLastTemporaryGlyphs; - // If set, info about the last call which wanted a prefix of the full text. - std::optional<CachedGlyphsKey> mLastPrefixKey; + // If set, info about the last call which wanted a substring of the full text. + std::optional<CachedGlyphsKey> mLastSubstringKey; SalLayoutGlyphsCache(const SalLayoutGlyphsCache&) = delete; SalLayoutGlyphsCache& operator=(const SalLayoutGlyphsCache&) = delete; diff --git a/vcl/source/gdi/impglyphitem.cxx b/vcl/source/gdi/impglyphitem.cxx index ed7e2895310e..b22170c80c60 100644 --- a/vcl/source/gdi/impglyphitem.cxx +++ b/vcl/source/gdi/impglyphitem.cxx @@ -304,7 +304,8 @@ SalLayoutGlyphsCache::GetLayoutGlyphs(VclPtr<const OutputDevice> outputDevice, c const SalLayoutFlags glyphItemsOnlyLayout = SalLayoutFlags::GlyphItemsOnly | SalLayoutFlags::BiDiStrong; #endif - bool resetLastPrefixKey = true; + bool resetLastSubstringKey = true; + const sal_Unicode nbSpace = 0xa0; // non-breaking space if (nIndex != 0 || nLen != text.getLength()) { // The glyphs functions are often called first for an entire string @@ -319,9 +320,13 @@ SalLayoutGlyphsCache::GetLayoutGlyphs(VclPtr<const OutputDevice> outputDevice, c { // This function may often be called repeatedly for segments of the same string, // in which case it is more efficient to cache glyphs for the entire string - // and then return subsets of them. So if the first call is for a prefix of the string, - // remember that, and if the next call follows the previous part of the string, - // cache the entire string. + // and then return subsets of them. So if a second call either starts at the same + // position or starts at the end of the previous call, cache the entire string. + // This used to do this only for the first two segments of the string, + // but that missed the case when the font slightly changed e.g. because of the first + // part being underlined. Doing this for any two segments allows this optimization + // even when the prefix of the string would use a different font. + // TODO: Can those font differences be ignored? // Writer layouts tests enable SAL_ABORT_ON_NON_APPLICATION_FONT_USE in order // to make PrintFontManager::Substitute() abort if font fallback happens. When // laying out the entire string the chance this happens increases (e.g. testAbi11870 @@ -330,28 +335,35 @@ SalLayoutGlyphsCache::GetLayoutGlyphs(VclPtr<const OutputDevice> outputDevice, c // does not change result of this function, simply disable it for those tests. static bool bAbortOnFontSubstitute = getenv("SAL_ABORT_ON_NON_APPLICATION_FONT_USE") != nullptr; - if (nIndex == 0) + if (mLastSubstringKey.has_value() && !bAbortOnFontSubstitute) { - mLastPrefixKey = key; - resetLastPrefixKey = false; + sal_Int32 pos = nIndex; + if (mLastSubstringKey->len < pos && text[pos - 1] == nbSpace) + --pos; // Writer skips a non-breaking space, so skip that character too. + if ((mLastSubstringKey->len == pos || mLastSubstringKey->index == nIndex) + && mLastSubstringKey + == CachedGlyphsKey(outputDevice, text, mLastSubstringKey->index, + mLastSubstringKey->len, nLogicWidth)) + { + std::unique_ptr<SalLayout> layout + = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), nLogicWidth, {}, + glyphItemsOnlyLayout, layoutCache); + GetLayoutGlyphs(outputDevice, text, 0, text.getLength(), nLogicWidth, + layoutCache); + itWhole = mCachedGlyphs.find(keyWhole); + } + else + mLastSubstringKey.reset(); } - else if (mLastPrefixKey.has_value() && mLastPrefixKey->len == nIndex - && mLastPrefixKey - == CachedGlyphsKey(outputDevice, text, mLastPrefixKey->index, - mLastPrefixKey->len, nLogicWidth) - && !bAbortOnFontSubstitute) + if (!mLastSubstringKey.has_value()) { - assert(mLastPrefixKey->index == 0); - std::unique_ptr<SalLayout> layout - = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), nLogicWidth, {}, - glyphItemsOnlyLayout, layoutCache); - GetLayoutGlyphs(outputDevice, text, 0, text.getLength(), nLogicWidth, layoutCache); - itWhole = mCachedGlyphs.find(keyWhole); + mLastSubstringKey = key; + resetLastSubstringKey = false; } } if (itWhole != mCachedGlyphs.end() && itWhole->second.IsValid()) { - mLastPrefixKey.reset(); + mLastSubstringKey.reset(); mLastTemporaryGlyphs = makeGlyphsSubset(itWhole->second, outputDevice, text, nIndex, nLen); if (mLastTemporaryGlyphs.IsValid()) @@ -376,8 +388,14 @@ SalLayoutGlyphsCache::GetLayoutGlyphs(VclPtr<const OutputDevice> outputDevice, c } } } - if (resetLastPrefixKey) - mLastPrefixKey.reset(); + if (resetLastSubstringKey) + { + // Writer does non-breaking space differently (not as part of the string), so in that + // case ignore that call and still allow finding two adjacent substrings that have + // the non-breaking space between them. + if (nLen != 1 || text[nIndex] != nbSpace) + mLastSubstringKey.reset(); + } std::shared_ptr<const vcl::text::TextLayoutCache> tmpLayoutCache; if (layoutCache == nullptr)