summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/o3tl/lru_map.hxx27
-rw-r--r--o3tl/qa/test-lru_map.cxx55
-rw-r--r--vcl/inc/fontinstance.hxx6
-rw-r--r--vcl/inc/impfontcache.hxx48
-rw-r--r--vcl/source/font/fontcache.cxx38
-rw-r--r--vcl/source/font/fontinstance.cxx13
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: */