Bump version to 21.06.18.1
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsPageCacheManager.cxx
blob45afd93c96f347569d46813e0dcc9b72906ee75d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <cache/SlsPageCacheManager.hxx>
22 #include "SlsBitmapCache.hxx"
24 #include <deque>
25 #include <map>
26 #include <memory>
27 #include <unordered_map>
29 namespace {
31 /** Collection of data that is stored for all active preview caches.
33 class CacheDescriptor
35 public:
36 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
37 Size maPreviewSize;
39 CacheDescriptor(
40 ::sd::slidesorter::cache::PageCacheManager::DocumentKey const & pDocument,
41 const Size& rPreviewSize)
42 :mpDocument(pDocument),maPreviewSize(rPreviewSize)
44 /// Test for equality with respect to all members.
45 class Equal {public: bool operator() (
46 const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
47 return rDescriptor1.mpDocument==rDescriptor2.mpDocument
48 && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
49 } };
50 /// Hash function that takes all members into account.
51 class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
52 return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width();
53 } };
56 /** Collection of data that is stored for the inactive, recently used
57 caches.
59 class RecentlyUsedCacheDescriptor
61 public:
62 Size maPreviewSize;
63 std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache;
65 RecentlyUsedCacheDescriptor(
66 const Size& rPreviewSize,
67 const std::shared_ptr< ::sd::slidesorter::cache::BitmapCache>& rpCache)
68 :maPreviewSize(rPreviewSize),mpCache(rpCache)
72 /** The list of recently used caches is organized as queue. When elements
73 are added the list is shortened to the maximally allowed number of
74 elements by removing the least recently used elements.
76 typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
78 /** Compare the caches by preview size. Those that match the given size
79 come first, then, regardless of the given size, the largest ones before
80 the smaller ones.
82 class BestFittingCacheComparer
84 public:
85 explicit BestFittingCacheComparer (const Size& rPreferredSize)
86 : maPreferredSize(rPreferredSize)
88 bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
89 const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
91 if (rElement2.first == maPreferredSize)
92 return false;
93 else if (rElement1.first == maPreferredSize)
94 return true;
95 else
96 return (rElement1.first.Width()*rElement1.first.Height()
97 > rElement2.first.Width()*rElement2.first.Height());
100 private:
101 Size maPreferredSize;
104 } // end of anonymous namespace
106 namespace sd::slidesorter::cache {
108 /** Container for the active caches.
110 class PageCacheManager::PageCacheContainer
111 : public std::unordered_map<CacheDescriptor,
112 std::shared_ptr<BitmapCache>,
113 CacheDescriptor::Hash,
114 CacheDescriptor::Equal>
116 public:
117 PageCacheContainer() {}
119 /** Compare entries in the cache container with respect to the cache
120 address only.
122 class CompareWithCache { public:
123 explicit CompareWithCache(const std::shared_ptr<BitmapCache>& rpCache)
124 : mpCache(rpCache) {}
125 bool operator () (const PageCacheContainer::value_type& rValue) const
126 { return rValue.second == mpCache; }
127 private:
128 std::shared_ptr<BitmapCache> mpCache;
132 /** The recently used caches are stored in one queue for each document.
134 class PageCacheManager::RecentlyUsedPageCaches
136 public:
137 typedef DocumentKey key_type;
138 typedef RecentlyUsedQueue mapped_type;
139 typedef std::map<key_type,mapped_type>::iterator iterator;
140 private:
141 std::map<key_type,mapped_type> maMap;
142 public:
143 RecentlyUsedPageCaches () {};
145 iterator end() { return maMap.end(); }
146 void clear() { maMap.clear(); }
147 iterator find(const key_type& key) { return maMap.find(key); }
148 template<class... Args>
149 std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
152 class PageCacheManager::Deleter
154 public:
155 void operator() (PageCacheManager* pObject) { delete pObject; }
158 //===== PageCacheManager ====================================================
160 std::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
162 std::shared_ptr<PageCacheManager> PageCacheManager::Instance()
164 std::shared_ptr<PageCacheManager> pInstance;
166 ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
168 pInstance = mpInstance.lock();
169 if (pInstance == nullptr)
171 pInstance = std::shared_ptr<PageCacheManager>(
172 new PageCacheManager(),
173 PageCacheManager::Deleter());
174 mpInstance = pInstance;
177 return pInstance;
180 PageCacheManager::PageCacheManager()
181 : mpPageCaches(new PageCacheContainer()),
182 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches())
186 PageCacheManager::~PageCacheManager()
190 std::shared_ptr<BitmapCache> PageCacheManager::GetCache (
191 const DocumentKey& pDocument,
192 const Size& rPreviewSize)
194 std::shared_ptr<BitmapCache> pResult;
196 // Look for the cache in the list of active caches.
197 CacheDescriptor aKey (pDocument, rPreviewSize);
198 PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
199 if (iCache != mpPageCaches->end())
200 pResult = iCache->second;
202 // Look for the cache in the list of recently used caches.
203 if (pResult == nullptr)
204 pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
206 // Create the cache when no suitable one does exist.
207 if (pResult == nullptr)
208 pResult = std::make_shared<BitmapCache>();
210 // The cache may be newly created and thus empty or is old and may
211 // contain previews that are not up-to-date. Recycle previews from
212 // other caches to fill in the holes.
213 Recycle(pResult, pDocument,rPreviewSize);
215 // Put the new (or old) cache into the container.
216 mpPageCaches->emplace(aKey, pResult);
218 return pResult;
221 void PageCacheManager::Recycle (
222 const std::shared_ptr<BitmapCache>& rpCache,
223 const DocumentKey& pDocument,
224 const Size& rPreviewSize)
226 BestFittingPageCaches aCaches;
228 // Add bitmap caches from active caches.
229 for (auto& rActiveCache : *mpPageCaches)
231 if (rActiveCache.first.mpDocument == pDocument)
232 aCaches.emplace_back(
233 rActiveCache.first.maPreviewSize, rActiveCache.second);
236 // Add bitmap caches from recently used caches.
237 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
238 if (iQueue != mpRecentlyUsedPageCaches->end())
240 for (const auto& rRecentCache : iQueue->second)
241 aCaches.emplace_back(
242 rRecentCache.maPreviewSize, rRecentCache.mpCache);
245 ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
247 for (const auto& rBestCache : aCaches)
249 rpCache->Recycle(*rBestCache.second);
253 void PageCacheManager::ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache)
255 PageCacheContainer::iterator iCache (::std::find_if(
256 mpPageCaches->begin(),
257 mpPageCaches->end(),
258 PageCacheContainer::CompareWithCache(rpCache)));
260 if (iCache != mpPageCaches->end())
262 assert(iCache->second == rpCache);
264 PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
266 mpPageCaches->erase(iCache);
270 std::shared_ptr<BitmapCache> PageCacheManager::ChangeSize (
271 const std::shared_ptr<BitmapCache>& rpCache,
272 const Size&,
273 const Size& rNewPreviewSize)
275 std::shared_ptr<BitmapCache> pResult;
277 if (rpCache != nullptr)
279 // Look up the given cache in the list of active caches.
280 PageCacheContainer::iterator iCacheToChange (::std::find_if(
281 mpPageCaches->begin(),
282 mpPageCaches->end(),
283 PageCacheContainer::CompareWithCache(rpCache)));
284 if (iCacheToChange != mpPageCaches->end())
286 assert(iCacheToChange->second == rpCache);
288 // Now, we can change the preview size of the existing one by
289 // removing the cache from the list and re-insert it with the
290 // updated size.
291 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
292 iCacheToChange->first.mpDocument);
293 mpPageCaches->erase(iCacheToChange);
294 mpPageCaches->emplace(
295 CacheDescriptor(aKey,rNewPreviewSize),
296 rpCache);
298 pResult = rpCache;
300 else
302 assert(iCacheToChange != mpPageCaches->end());
306 return pResult;
309 bool PageCacheManager::InvalidatePreviewBitmap (
310 const DocumentKey& pDocument,
311 const SdrPage* pKey)
313 bool bHasChanged (false);
315 if (pDocument!=nullptr)
317 // Iterate over all caches that are currently in use and invalidate
318 // the previews in those that belong to the document.
319 for (auto& rCache : *mpPageCaches)
320 if (rCache.first.mpDocument == pDocument)
321 bHasChanged |= rCache.second->InvalidateBitmap(pKey);
323 // Invalidate the previews in the recently used caches belonging to
324 // the given document.
325 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
326 if (iQueue != mpRecentlyUsedPageCaches->end())
328 for (const auto& rCache2 : iQueue->second)
329 bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey);
333 return bHasChanged;
336 void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey& pDocument)
338 if (pDocument == nullptr)
339 return;
341 // Iterate over all caches that are currently in use and invalidate the
342 // previews in those that belong to the document.
343 for (auto& rCache : *mpPageCaches)
344 if (rCache.first.mpDocument == pDocument)
345 rCache.second->InvalidateCache();
347 // Invalidate the previews in the recently used caches belonging to the
348 // given document.
349 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
350 if (iQueue != mpRecentlyUsedPageCaches->end())
352 for (const auto& rCache2 : iQueue->second)
353 rCache2.mpCache->InvalidateCache();
357 void PageCacheManager::InvalidateAllCaches()
359 // Iterate over all caches that are currently in use and invalidate
360 // them.
361 for (auto& rCache : *mpPageCaches)
362 rCache.second->InvalidateCache();
364 // Remove all recently used caches, there is not much sense in storing
365 // invalidated and unused caches.
366 mpRecentlyUsedPageCaches->clear();
369 void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage)
371 for (auto& rCache : *mpPageCaches)
372 rCache.second->ReleaseBitmap(pPage);
375 std::shared_ptr<BitmapCache> PageCacheManager::GetRecentlyUsedCache (
376 const DocumentKey& pDocument,
377 const Size& rPreviewSize)
379 std::shared_ptr<BitmapCache> pCache;
381 // Look for the cache in the list of recently used caches.
382 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
383 if (iQueue != mpRecentlyUsedPageCaches->end())
385 RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(),
386 [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; });
387 if (iCache != iQueue->second.end())
389 pCache = iCache->mpCache;
390 iQueue->second.erase(iCache);
394 return pCache;
397 void PageCacheManager::PutRecentlyUsedCache(
398 DocumentKey const & pDocument,
399 const Size& rPreviewSize,
400 const std::shared_ptr<BitmapCache>& rpCache)
402 // Look up the list of recently used caches for the given document.
403 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
404 if (iQueue == mpRecentlyUsedPageCaches->end())
405 iQueue = mpRecentlyUsedPageCaches->emplace(
406 pDocument, RecentlyUsedQueue()
407 ).first;
409 if (iQueue != mpRecentlyUsedPageCaches->end())
411 iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache));
412 // Shorten the list of recently used caches to the allowed maximal length.
413 while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
414 iQueue->second.pop_back();
418 } // end of namespace ::sd::slidesorter::cache
420 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */