diff options
-rw-r--r-- | include/o3tl/lru_map.hxx | 27 | ||||
-rw-r--r-- | o3tl/qa/test-lru_map.cxx | 55 | ||||
-rw-r--r-- | vcl/inc/fontinstance.hxx | 6 | ||||
-rw-r--r-- | vcl/inc/impfontcache.hxx | 48 | ||||
-rw-r--r-- | vcl/source/font/fontcache.cxx | 38 | ||||
-rw-r--r-- | vcl/source/font/fontinstance.cxx | 13 |
6 files changed, 179 insertions, 8 deletions
diff --git a/include/o3tl/lru_map.hxx b/include/o3tl/lru_map.hxx index 015120e31cb3..79e81293a858 100644 --- a/include/o3tl/lru_map.hxx +++ b/include/o3tl/lru_map.hxx @@ -34,8 +34,10 @@ namespace o3tl template<typename Key, typename Value, class KeyHash = std::hash<Key>> class lru_map final { -private: +public: typedef typename std::pair<Key, Value> key_value_pair_t; + +private: typedef std::list<key_value_pair_t> list_t; typedef typename list_t::iterator list_iterator_t; typedef typename list_t::const_iterator list_const_iterator_t; @@ -58,6 +60,7 @@ private: mLruList.pop_back(); } } + public: typedef list_iterator_t iterator; typedef list_const_iterator_t const_iterator; @@ -126,6 +129,28 @@ public: } } + // reverse-iterates the list removing all items matching the predicate + template<class UnaryPredicate> + void remove_if(UnaryPredicate pred) + { + auto it = mLruList.rbegin(); + while (it != mLruList.rend()) + { + if (pred(*it)) + { + mLruMap.erase(it->first); + it = decltype(it){ mLruList.erase(std::next(it).base()) }; + } + else + ++it; + } + } + + const list_const_iterator_t begin() const + { + return mLruList.cbegin(); + } + const list_const_iterator_t end() const { return mLruList.cend(); diff --git a/o3tl/qa/test-lru_map.cxx b/o3tl/qa/test-lru_map.cxx index 096b21ec5189..7a4886d3be3e 100644 --- a/o3tl/qa/test-lru_map.cxx +++ b/o3tl/qa/test-lru_map.cxx @@ -27,6 +27,7 @@ public: void testReplaceValue(); void testLruRemoval(); void testCustomHash(); + void testRemoveIf(); CPPUNIT_TEST_SUITE(lru_map_test); CPPUNIT_TEST(testBaseUsage); @@ -34,6 +35,7 @@ public: CPPUNIT_TEST(testReplaceValue); CPPUNIT_TEST(testLruRemoval); CPPUNIT_TEST(testCustomHash); + CPPUNIT_TEST(testRemoveIf); CPPUNIT_TEST_SUITE_END(); }; @@ -234,6 +236,59 @@ void lru_map_test::testCustomHash() CPPUNIT_ASSERT_EQUAL(13, lru.find(TestClassKey(2,1))->second); } +void lru_map_test::testRemoveIf() +{ + typedef o3tl::lru_map<int, int> IntMap; + typedef IntMap::key_value_pair_t IntMapPair; + struct limit_except : public std::exception {}; + + IntMap lru(6); + int i = 0; + for (; i < 8; i++) + lru.insert({i, i}); + CPPUNIT_ASSERT_EQUAL(size_t(6), lru.size()); + // now contains 7..2 + + // remove everything < 4 from the back + try + { + lru.remove_if([] (IntMapPair const& rPair) { + if (rPair.first >= 4) + throw limit_except(); + return true; + }); + CPPUNIT_ASSERT(false); // not reached + } + catch (limit_except) + { + // contains 7..4 + CPPUNIT_ASSERT_EQUAL(size_t(4), lru.size()); + } + + // remove all even numbers + lru.remove_if([] (IntMapPair const& rPair) { return (0 == rPair.first % 2); }); + CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size()); + // contains 7, 5 + + lru.insert({5, 5}); + // contains 5, 7 + + i = 5; + for (auto &rPair : lru) + { + CPPUNIT_ASSERT_EQUAL(i, rPair.first); + i += 2; + } + + // remove the first item + lru.remove_if([] (IntMapPair const& rPair) { return (rPair.first == 5); }); + CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size()); + + // remove the only item + lru.remove_if([] (IntMapPair const& rPair) { return (rPair.first == 7); }); + CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size()); +} + CPPUNIT_TEST_SUITE_REGISTRATION(lru_map_test); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/fontinstance.hxx b/vcl/inc/fontinstance.hxx index 597c747553ac..b3b67c8a9079 100644 --- a/vcl/inc/fontinstance.hxx +++ b/vcl/inc/fontinstance.hxx @@ -25,6 +25,9 @@ #include <rtl/ref.hxx> #include <salhelper/simplereferenceobject.hxx> +#include <tools/gen.hxx> +#include <vcl/vcllayout.hxx> + #include <unordered_map> #include <memory> @@ -67,6 +70,9 @@ public: // TODO: make data members private const PhysicalFontFace* GetFontFace() const { return m_pFontFace.get(); } const ImplFontCache* GetFontCache() const { return mpFontCache; } + bool GetCachedGlyphBoundRect(sal_GlyphId, tools::Rectangle &); + void CacheGlyphBoundRect(sal_GlyphId nID, tools::Rectangle &); + int GetKashidaWidth(); void GetScale(double* nXScale, double* nYScale); diff --git a/vcl/inc/impfontcache.hxx b/vcl/inc/impfontcache.hxx index 10a38540e67b..c96994300176 100644 --- a/vcl/inc/impfontcache.hxx +++ b/vcl/inc/impfontcache.hxx @@ -21,6 +21,11 @@ #define INCLUDED_VCL_INC_IMPFONTCACHE_HXX #include <unordered_map> +#include <boost/functional/hash.hpp> + +#include <o3tl/lru_map.hxx> +#include <tools/gen.hxx> +#include <vcl/vcllayout.hxx> #include "fontselect.hxx" @@ -28,25 +33,53 @@ class Size; namespace vcl { class Font; } class PhysicalFontCollection; - // TODO: closely couple with PhysicalFontCollection +struct GlpyhBoundRectCacheKey +{ + const LogicalFontInstance* m_pFont; + const sal_GlyphId m_nId; + + GlpyhBoundRectCacheKey(const LogicalFontInstance* pFont, sal_GlyphId nID) + : m_pFont(pFont), m_nId(nID) + {} + + bool operator==(GlpyhBoundRectCacheKey const& aOther) const + { return m_pFont == aOther.m_pFont && m_nId == aOther.m_nId; } +}; + +struct GlpyhBoundRectCacheHash +{ + std::size_t operator()(GlpyhBoundRectCacheKey const& aCache) const + { + std::size_t seed = 0; + boost::hash_combine(seed, aCache.m_pFont); + boost::hash_combine(seed, aCache.m_nId); + return seed; + } +}; + +typedef o3tl::lru_map<GlpyhBoundRectCacheKey, tools::Rectangle, + GlpyhBoundRectCacheHash> GlpyhBoundRectCache; +typedef GlpyhBoundRectCache::key_value_pair_t GlpyhBoundRectCachePair; + class ImplFontCache { private: - LogicalFontInstance* mpLastHitCacheEntry; ///< keeps the last hit cache entry - // cache of recently used font instances struct IFSD_Equal { bool operator()( const FontSelectPattern&, const FontSelectPattern& ) const; }; struct IFSD_Hash { size_t operator()( const FontSelectPattern& ) const; }; typedef std::unordered_map<FontSelectPattern, rtl::Reference<LogicalFontInstance>, IFSD_Hash, IFSD_Equal> FontInstanceList; - FontInstanceList maFontInstanceList; + + LogicalFontInstance* mpLastHitCacheEntry; ///< keeps the last hit cache entry + FontInstanceList maFontInstanceList; + GlpyhBoundRectCache m_aBoundRectCache; rtl::Reference<LogicalFontInstance> GetFontInstance(PhysicalFontCollection const*, FontSelectPattern&); public: - ImplFontCache(); - ~ImplFontCache(); + ImplFontCache(); + ~ImplFontCache(); rtl::Reference<LogicalFontInstance> GetFontInstance( PhysicalFontCollection const *, const vcl::Font&, const Size& rPixelSize, float fExactHeight); @@ -54,6 +87,9 @@ public: LogicalFontInstance* pLogicalFont, int nFallbackLevel, OUString& rMissingCodes ); + bool GetCachedGlyphBoundRect(LogicalFontInstance *, sal_GlyphId, tools::Rectangle &); + void CacheGlyphBoundRect(LogicalFontInstance *, sal_GlyphId, tools::Rectangle &); + void Invalidate(); }; diff --git a/vcl/source/font/fontcache.cxx b/vcl/source/font/fontcache.cxx index 82a6261c274c..0db997fe510f 100644 --- a/vcl/source/font/fontcache.cxx +++ b/vcl/source/font/fontcache.cxx @@ -84,11 +84,15 @@ bool ImplFontCache::IFSD_Equal::operator()(const FontSelectPattern& rA, const Fo } ImplFontCache::ImplFontCache() -: mpLastHitCacheEntry( nullptr ) + : mpLastHitCacheEntry( nullptr ) + // The cache limit is set by the rough number of characters needed to read your average Asian newspaper. + , m_aBoundRectCache(3000) {} ImplFontCache::~ImplFontCache() { + for (auto & rLFI : maFontInstanceList) + rLFI.second->mpFontCache = nullptr; } rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList, @@ -174,6 +178,9 @@ rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFont ++it_next; continue; } + m_aBoundRectCache.remove_if([&pFontEntry] (GlpyhBoundRectCachePair const& rPair) + { return rPair.first.m_pFont == pFontEntry; } ); + maFontInstanceList.erase(it_next); if (mpLastHitCacheEntry == pFontEntry) mpLastHitCacheEntry = nullptr; @@ -233,6 +240,35 @@ void ImplFontCache::Invalidate() for (auto const & pair : maFontInstanceList) pair.second->mpFontCache = nullptr; maFontInstanceList.clear(); + m_aBoundRectCache.clear(); +} + +bool ImplFontCache::GetCachedGlyphBoundRect(LogicalFontInstance *pFont, sal_GlyphId nID, tools::Rectangle &rRect) +{ + if (!pFont->GetFontCache()) + return false; + assert(pFont->GetFontCache() == this); + if (pFont->GetFontCache() != this) + return false; + + auto it = m_aBoundRectCache.find({pFont, nID}); + if (it != m_aBoundRectCache.end()) + { + rRect = it->second; + return true; + } + return false; +} + +void ImplFontCache::CacheGlyphBoundRect(LogicalFontInstance *pFont, sal_GlyphId nID, tools::Rectangle &rRect) +{ + if (!pFont->GetFontCache()) + return; + assert(pFont->GetFontCache() == this); + if (pFont->GetFontCache() != this) + return; + + m_aBoundRectCache.insert({{pFont, nID}, rRect}); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/font/fontinstance.cxx b/vcl/source/font/fontinstance.cxx index cf80428f61e2..7a889fa65a7c 100644 --- a/vcl/source/font/fontinstance.cxx +++ b/vcl/source/font/fontinstance.cxx @@ -147,5 +147,18 @@ void LogicalFontInstance::IgnoreFallbackForUnicode( sal_UCS4 cChar, FontWeight e mpUnicodeFallbackList->erase( it ); } +bool LogicalFontInstance::GetCachedGlyphBoundRect(sal_GlyphId nID, tools::Rectangle &rRect) +{ + if (!mpFontCache) + return false; + return mpFontCache->GetCachedGlyphBoundRect(this, nID, rRect); +} + +void LogicalFontInstance::CacheGlyphBoundRect(sal_GlyphId nID, tools::Rectangle &rRect) +{ + if (!mpFontCache) + return; + mpFontCache->CacheGlyphBoundRect(this, nID, rRect); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |