[Libreoffice-commits] core.git: include/o3tl o3tl/qa vcl/inc vcl/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Sat Oct 6 13:18:12 UTC 2018


 include/o3tl/lru_map.hxx         |   27 ++++++++++++++++++-
 o3tl/qa/test-lru_map.cxx         |   55 +++++++++++++++++++++++++++++++++++++++
 vcl/inc/fontinstance.hxx         |    6 ++++
 vcl/inc/impfontcache.hxx         |   48 +++++++++++++++++++++++++++++-----
 vcl/source/font/fontcache.cxx    |   38 ++++++++++++++++++++++++++
 vcl/source/font/fontinstance.cxx |   13 +++++++++
 6 files changed, 179 insertions(+), 8 deletions(-)

New commits:
commit 612339e574293b248c44cc04a4fae0c77a64ad53
Author:     Jan-Marek Glogowski <glogow at fbihome.de>
AuthorDate: Thu Oct 4 14:03:07 2018 +0200
Commit:     Jan-Marek Glogowski <glogow at fbihome.de>
CommitDate: Sat Oct 6 15:17:52 2018 +0200

    Add a glyph-bound-rect cache to the font cache
    
    This way the font cache can correctly invalidate the cached glyph
    rects when a font is dropped from the cache.
    
    Change-Id: I050866099742334f01cac1b872228a017ddb5e9b
    Reviewed-on: https://gerrit.libreoffice.org/61371
    Tested-by: Jenkins
    Reviewed-by: Jan-Marek Glogowski <glogow at fbihome.de>

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: */


More information about the Libreoffice-commits mailing list