bump product version to 6.3.0.0.beta1
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsPageCacheManager.cxx
blob3077acb40a1916dcc7ffc54d616d7420900b7900
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"
23 #include <view/SlideSorterView.hxx>
24 #include <model/SlideSorterModel.hxx>
26 #include <deque>
27 #include <map>
28 #include <memory>
30 namespace {
32 /** Collection of data that is stored for all active preview caches.
34 class CacheDescriptor
36 public:
37 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
38 Size const maPreviewSize;
40 CacheDescriptor(
41 ::sd::slidesorter::cache::PageCacheManager::DocumentKey const & pDocument,
42 const Size& rPreviewSize)
43 :mpDocument(pDocument),maPreviewSize(rPreviewSize)
45 /// Test for equality with respect to all members.
46 class Equal {public: bool operator() (
47 const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
48 return rDescriptor1.mpDocument==rDescriptor2.mpDocument
49 && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
50 } };
51 /// Hash function that takes all members into account.
52 class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
53 return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width();
54 } };
57 /** Collection of data that is stored for the inactive, recently used
58 caches.
60 class RecentlyUsedCacheDescriptor
62 public:
63 Size maPreviewSize;
64 std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache;
66 RecentlyUsedCacheDescriptor(
67 const Size& rPreviewSize,
68 const std::shared_ptr< ::sd::slidesorter::cache::BitmapCache>& rpCache)
69 :maPreviewSize(rPreviewSize),mpCache(rpCache)
73 /** The list of recently used caches is organized as queue. When elements
74 are added the list is shortened to the maximally allowed number of
75 elements by removing the least recently used elements.
77 typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
79 /** Compare the caches by preview size. Those that match the given size
80 come first, then, regardless of the given size, the largest ones before
81 the smaller ones.
83 class BestFittingCacheComparer
85 public:
86 explicit BestFittingCacheComparer (const Size& rPreferredSize)
87 : maPreferredSize(rPreferredSize)
89 bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
90 const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
92 if (rElement2.first == maPreferredSize)
93 return false;
94 else if (rElement1.first == maPreferredSize)
95 return true;
96 else
97 return (rElement1.first.Width()*rElement1.first.Height()
98 > rElement2.first.Width()*rElement2.first.Height());
101 private:
102 Size const maPreferredSize;
105 } // end of anonymous namespace
107 namespace sd { namespace slidesorter { namespace cache {
109 /** Container for the active caches.
111 class PageCacheManager::PageCacheContainer
112 : public std::unordered_map<CacheDescriptor,
113 std::shared_ptr<BitmapCache>,
114 CacheDescriptor::Hash,
115 CacheDescriptor::Equal>
117 public:
118 PageCacheContainer() {}
120 /** Compare entries in the cache container with respect to the cache
121 address only.
123 class CompareWithCache { public:
124 explicit CompareWithCache(const std::shared_ptr<BitmapCache>& rpCache)
125 : mpCache(rpCache) {}
126 bool operator () (const PageCacheContainer::value_type& rValue) const
127 { return rValue.second == mpCache; }
128 private:
129 std::shared_ptr<BitmapCache> mpCache;
133 /** The recently used caches are stored in one queue for each document.
135 class PageCacheManager::RecentlyUsedPageCaches
137 public:
138 typedef DocumentKey key_type;
139 typedef RecentlyUsedQueue mapped_type;
140 typedef std::pair<const key_type,mapped_type> value_type;
141 typedef std::map<key_type,mapped_type>::iterator iterator;
142 private:
143 std::map<key_type,mapped_type> maMap;
144 public:
145 RecentlyUsedPageCaches () {};
147 iterator end() { return maMap.end(); }
148 void clear() { maMap.clear(); }
149 iterator find(const key_type& key) { return maMap.find(key); }
150 template<class... Args>
151 std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
154 class PageCacheManager::Deleter
156 public:
157 void operator() (PageCacheManager* pObject) { delete pObject; }
160 //===== PageCacheManager ====================================================
162 std::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
164 std::shared_ptr<PageCacheManager> PageCacheManager::Instance()
166 std::shared_ptr<PageCacheManager> pInstance;
168 ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
170 pInstance = mpInstance.lock();
171 if (pInstance == nullptr)
173 pInstance = std::shared_ptr<PageCacheManager>(
174 new PageCacheManager(),
175 PageCacheManager::Deleter());
176 mpInstance = pInstance;
179 return pInstance;
182 PageCacheManager::PageCacheManager()
183 : mpPageCaches(new PageCacheContainer()),
184 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches())
188 PageCacheManager::~PageCacheManager()
192 std::shared_ptr<BitmapCache> PageCacheManager::GetCache (
193 const DocumentKey& pDocument,
194 const Size& rPreviewSize)
196 std::shared_ptr<BitmapCache> pResult;
198 // Look for the cache in the list of active caches.
199 CacheDescriptor aKey (pDocument, rPreviewSize);
200 PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
201 if (iCache != mpPageCaches->end())
202 pResult = iCache->second;
204 // Look for the cache in the list of recently used caches.
205 if (pResult == nullptr)
206 pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
208 // Create the cache when no suitable one does exist.
209 if (pResult == nullptr)
210 pResult.reset(new BitmapCache());
212 // The cache may be newly created and thus empty or is old and may
213 // contain previews that are not up-to-date. Recycle previews from
214 // other caches to fill in the holes.
215 Recycle(pResult, pDocument,rPreviewSize);
217 // Put the new (or old) cache into the container.
218 mpPageCaches->emplace(aKey, pResult);
220 return pResult;
223 void PageCacheManager::Recycle (
224 const std::shared_ptr<BitmapCache>& rpCache,
225 const DocumentKey& pDocument,
226 const Size& rPreviewSize)
228 BestFittingPageCaches aCaches;
230 // Add bitmap caches from active caches.
231 for (auto& rActiveCache : *mpPageCaches)
233 if (rActiveCache.first.mpDocument == pDocument)
234 aCaches.emplace_back(
235 rActiveCache.first.maPreviewSize, rActiveCache.second);
238 // Add bitmap caches from recently used caches.
239 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
240 if (iQueue != mpRecentlyUsedPageCaches->end())
242 for (const auto& rRecentCache : iQueue->second)
243 aCaches.emplace_back(
244 rRecentCache.maPreviewSize, rRecentCache.mpCache);
247 ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
249 for (const auto& rBestCache : aCaches)
251 rpCache->Recycle(*rBestCache.second);
255 void PageCacheManager::ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache)
257 PageCacheContainer::iterator iCache (::std::find_if(
258 mpPageCaches->begin(),
259 mpPageCaches->end(),
260 PageCacheContainer::CompareWithCache(rpCache)));
262 if (iCache != mpPageCaches->end())
264 assert(iCache->second == rpCache);
266 PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
268 mpPageCaches->erase(iCache);
272 std::shared_ptr<BitmapCache> PageCacheManager::ChangeSize (
273 const std::shared_ptr<BitmapCache>& rpCache,
274 const Size&,
275 const Size& rNewPreviewSize)
277 std::shared_ptr<BitmapCache> pResult;
279 if (rpCache != nullptr)
281 // Look up the given cache in the list of active caches.
282 PageCacheContainer::iterator iCacheToChange (::std::find_if(
283 mpPageCaches->begin(),
284 mpPageCaches->end(),
285 PageCacheContainer::CompareWithCache(rpCache)));
286 if (iCacheToChange != mpPageCaches->end())
288 assert(iCacheToChange->second == rpCache);
290 // Now, we can change the preview size of the existing one by
291 // removing the cache from the list and re-insert it with the
292 // updated size.
293 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
294 iCacheToChange->first.mpDocument);
295 mpPageCaches->erase(iCacheToChange);
296 mpPageCaches->emplace(
297 CacheDescriptor(aKey,rNewPreviewSize),
298 rpCache);
300 pResult = rpCache;
302 else
304 assert(iCacheToChange != mpPageCaches->end());
308 return pResult;
311 bool PageCacheManager::InvalidatePreviewBitmap (
312 const DocumentKey& pDocument,
313 const SdrPage* pKey)
315 bool bHasChanged (false);
317 if (pDocument!=nullptr)
319 // Iterate over all caches that are currently in use and invalidate
320 // the previews in those that belong to the document.
321 PageCacheContainer::iterator iCache;
322 for (auto& rCache : *mpPageCaches)
323 if (rCache.first.mpDocument == pDocument)
324 bHasChanged |= rCache.second->InvalidateBitmap(pKey);
326 // Invalidate the previews in the recently used caches belonging to
327 // the given document.
328 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
329 if (iQueue != mpRecentlyUsedPageCaches->end())
331 for (const auto& rCache2 : iQueue->second)
332 bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey);
336 return bHasChanged;
339 void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey& pDocument)
341 if (pDocument == nullptr)
342 return;
344 // Iterate over all caches that are currently in use and invalidate the
345 // previews in those that belong to the document.
346 for (auto& rCache : *mpPageCaches)
347 if (rCache.first.mpDocument == pDocument)
348 rCache.second->InvalidateCache();
350 // Invalidate the previews in the recently used caches belonging to the
351 // given document.
352 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
353 if (iQueue != mpRecentlyUsedPageCaches->end())
355 for (const auto& rCache2 : iQueue->second)
356 rCache2.mpCache->InvalidateCache();
360 void PageCacheManager::InvalidateAllCaches()
362 // Iterate over all caches that are currently in use and invalidate
363 // them.
364 for (auto& rCache : *mpPageCaches)
365 rCache.second->InvalidateCache();
367 // Remove all recently used caches, there is not much sense in storing
368 // invalidated and unused caches.
369 mpRecentlyUsedPageCaches->clear();
372 void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage)
374 for (auto& rCache : *mpPageCaches)
375 rCache.second->ReleaseBitmap(pPage);
378 std::shared_ptr<BitmapCache> PageCacheManager::GetRecentlyUsedCache (
379 const DocumentKey& pDocument,
380 const Size& rPreviewSize)
382 std::shared_ptr<BitmapCache> pCache;
384 // Look for the cache in the list of recently used caches.
385 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
386 if (iQueue != mpRecentlyUsedPageCaches->end())
388 RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(),
389 [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; });
390 if (iCache != iQueue->second.end())
392 pCache = iCache->mpCache;
393 iQueue->second.erase(iCache);
397 return pCache;
400 void PageCacheManager::PutRecentlyUsedCache(
401 DocumentKey const & pDocument,
402 const Size& rPreviewSize,
403 const std::shared_ptr<BitmapCache>& rpCache)
405 // Look up the list of recently used caches for the given document.
406 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
407 if (iQueue == mpRecentlyUsedPageCaches->end())
408 iQueue = mpRecentlyUsedPageCaches->emplace(
409 pDocument, RecentlyUsedQueue()
410 ).first;
412 if (iQueue != mpRecentlyUsedPageCaches->end())
414 iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache));
415 // Shorten the list of recently used caches to the allowed maximal length.
416 while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
417 iQueue->second.pop_back();
421 } } } // end of namespace ::sd::slidesorter::cache
423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */