LibreOffice Module sd (master) 1
SlsPageCacheManager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21
22#include "SlsBitmapCache.hxx"
23
24#include <deque>
25#include <map>
26#include <memory>
27#include <unordered_map>
28#include <utility>
29
30#include <comphelper/lok.hxx>
31
32namespace {
33
36class CacheDescriptor
37{
38public:
40 Size maPreviewSize;
41
42 CacheDescriptor(
44 const Size& rPreviewSize)
45 :mpDocument(std::move(pDocument)),maPreviewSize(rPreviewSize)
46 {}
48 class Equal {public: bool operator() (
49 const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
50 return rDescriptor1.mpDocument==rDescriptor2.mpDocument
51 && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
52 } };
54 class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
55 return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width();
56 } };
57};
58
62class RecentlyUsedCacheDescriptor
63{
64public:
65 Size maPreviewSize;
66 std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache;
67
68 RecentlyUsedCacheDescriptor(
69 const Size& rPreviewSize,
70 std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> pCache)
71 :maPreviewSize(rPreviewSize),mpCache(std::move(pCache))
72 {}
73};
74
79typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
80
85class BestFittingCacheComparer
86{
87public:
88 explicit BestFittingCacheComparer (const Size& rPreferredSize)
89 : maPreferredSize(rPreferredSize)
90 {}
91 bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
92 const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
93 {
94 if (rElement2.first == maPreferredSize)
95 return false;
96 else if (rElement1.first == maPreferredSize)
97 return true;
98 else
99 return (rElement1.first.Width()*rElement1.first.Height()
100 > rElement2.first.Width()*rElement2.first.Height());
101 }
102
103private:
104 Size maPreferredSize;
105};
106
107} // end of anonymous namespace
108
109namespace sd::slidesorter::cache {
110
114 : public std::unordered_map<CacheDescriptor,
115 std::shared_ptr<BitmapCache>,
116 CacheDescriptor::Hash,
117 CacheDescriptor::Equal>
118{
119public:
121
125 class CompareWithCache { public:
126 explicit CompareWithCache(std::shared_ptr<BitmapCache> pCache)
127 : mpCache(std::move(pCache)) {}
128 bool operator () (const PageCacheContainer::value_type& rValue) const
129 { return rValue.second == mpCache; }
130 private:
131 std::shared_ptr<BitmapCache> mpCache;
132 };
133};
134
138{
139public:
141 typedef RecentlyUsedQueue mapped_type;
142 typedef std::map<key_type,mapped_type>::iterator iterator;
143private:
144 std::map<key_type,mapped_type> maMap;
145public:
147
148 iterator end() { return maMap.end(); }
149 void clear() { maMap.clear(); }
150 iterator find(const key_type& key) { return maMap.find(key); }
151 template<class... Args>
152 std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
153};
154
156{
157public:
158 void operator() (PageCacheManager* pObject) { delete pObject; }
159};
160
161//===== PageCacheManager ====================================================
162
163std::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
164
165std::shared_ptr<PageCacheManager> PageCacheManager::Instance()
166{
167 std::shared_ptr<PageCacheManager> pInstance;
168
169 ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
170
171 pInstance = mpInstance.lock();
172 if (pInstance == nullptr)
173 {
174 pInstance = std::shared_ptr<PageCacheManager>(
175 new PageCacheManager(),
177 mpInstance = pInstance;
178 }
179
180 return pInstance;
181}
182
184 : mpPageCaches(new PageCacheContainer()),
185 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches())
186{
187}
188
190{
191}
192
193std::shared_ptr<BitmapCache> PageCacheManager::GetCache (
194 const DocumentKey& pDocument,
195 const Size& rPreviewSize)
196{
197 std::shared_ptr<BitmapCache> pResult;
198
199 // Look for the cache in the list of active caches.
200 CacheDescriptor aKey (pDocument, rPreviewSize);
201 PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
202 if (iCache != mpPageCaches->end())
203 pResult = iCache->second;
204
205 // Look for the cache in the list of recently used caches.
206 if (pResult == nullptr)
207 pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
208
209 // Create the cache when no suitable one does exist.
210 if (pResult == nullptr)
211 pResult = std::make_shared<BitmapCache>();
212
213 // The cache may be newly created and thus empty or is old and may
214 // contain previews that are not up-to-date. Recycle previews from
215 // other caches to fill in the holes.
216 Recycle(pResult, pDocument,rPreviewSize);
217
218 // Put the new (or old) cache into the container.
219 mpPageCaches->emplace(aKey, pResult);
220
221 return pResult;
222}
223
225 const std::shared_ptr<BitmapCache>& rpCache,
226 const DocumentKey& pDocument,
227 const Size& rPreviewSize)
228{
229 BestFittingPageCaches aCaches;
230
231 // Add bitmap caches from active caches.
232 for (auto& rActiveCache : *mpPageCaches)
233 {
234 if (rActiveCache.first.mpDocument == pDocument)
235 aCaches.emplace_back(
236 rActiveCache.first.maPreviewSize, rActiveCache.second);
237 }
238
239 // Add bitmap caches from recently used caches.
241 if (iQueue != mpRecentlyUsedPageCaches->end())
242 {
243 for (const auto& rRecentCache : iQueue->second)
244 aCaches.emplace_back(
245 rRecentCache.maPreviewSize, rRecentCache.mpCache);
246 }
247
248 ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
249
250 for (const auto& rBestCache : aCaches)
251 {
252 rpCache->Recycle(*rBestCache.second);
253 }
254}
255
256void PageCacheManager::ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache)
257{
258 PageCacheContainer::iterator iCache (::std::find_if(
259 mpPageCaches->begin(),
260 mpPageCaches->end(),
262
263 if (iCache != mpPageCaches->end())
264 {
265 assert(iCache->second == rpCache);
266
267 PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
268
269 mpPageCaches->erase(iCache);
270 }
271}
272
273std::shared_ptr<BitmapCache> PageCacheManager::ChangeSize (
274 const std::shared_ptr<BitmapCache>& rpCache,
275 const Size&,
276 const Size& rNewPreviewSize)
277{
278 std::shared_ptr<BitmapCache> pResult;
279
280 if (rpCache != nullptr)
281 {
282 // Look up the given cache in the list of active caches.
283 PageCacheContainer::iterator iCacheToChange (::std::find_if(
284 mpPageCaches->begin(),
285 mpPageCaches->end(),
287 if (iCacheToChange != mpPageCaches->end())
288 {
289 assert(iCacheToChange->second == rpCache);
290
291 // Now, we can change the preview size of the existing one by
292 // removing the cache from the list and re-insert it with the
293 // updated size.
294 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
295 iCacheToChange->first.mpDocument);
296 mpPageCaches->erase(iCacheToChange);
297 mpPageCaches->emplace(
298 CacheDescriptor(aKey,rNewPreviewSize),
299 rpCache);
300
301 pResult = rpCache;
302 }
303 // In multi user view this can happen - no issue (reset after switching MasterPage)
305 {
306 assert(iCacheToChange != mpPageCaches->end());
307 }
308 }
309
310 return pResult;
311}
312
314 const DocumentKey& pDocument,
315 const SdrPage* pKey)
316{
317 bool bHasChanged (false);
318
319 if (pDocument!=nullptr)
320 {
321 // Iterate over all caches that are currently in use and invalidate
322 // the previews in those that belong to the document.
323 for (auto& rCache : *mpPageCaches)
324 if (rCache.first.mpDocument == pDocument)
325 bHasChanged |= rCache.second->InvalidateBitmap(pKey);
326
327 // Invalidate the previews in the recently used caches belonging to
328 // the given document.
330 if (iQueue != mpRecentlyUsedPageCaches->end())
331 {
332 for (const auto& rCache2 : iQueue->second)
333 bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey);
334 }
335 }
336
337 return bHasChanged;
338}
339
341{
342 if (pDocument == nullptr)
343 return;
344
345 // Iterate over all caches that are currently in use and invalidate the
346 // previews in those that belong to the document.
347 for (auto& rCache : *mpPageCaches)
348 if (rCache.first.mpDocument == pDocument)
349 rCache.second->InvalidateCache();
350
351 // Invalidate the previews in the recently used caches belonging to the
352 // given document.
354 if (iQueue != mpRecentlyUsedPageCaches->end())
355 {
356 for (const auto& rCache2 : iQueue->second)
357 rCache2.mpCache->InvalidateCache();
358 }
359}
360
362{
363 // Iterate over all caches that are currently in use and invalidate
364 // them.
365 for (auto& rCache : *mpPageCaches)
366 rCache.second->InvalidateCache();
367
368 // Remove all recently used caches, there is not much sense in storing
369 // invalidated and unused caches.
371}
372
374{
375 for (auto& rCache : *mpPageCaches)
376 rCache.second->ReleaseBitmap(pPage);
377}
378
379std::shared_ptr<BitmapCache> PageCacheManager::GetRecentlyUsedCache (
380 const DocumentKey& pDocument,
381 const Size& rPreviewSize)
382{
383 std::shared_ptr<BitmapCache> pCache;
384
385 // Look for the cache in the list of recently used caches.
387 if (iQueue != mpRecentlyUsedPageCaches->end())
388 {
389 RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(),
390 [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; });
391 if (iCache != iQueue->second.end())
392 {
393 pCache = iCache->mpCache;
394 iQueue->second.erase(iCache);
395 }
396 }
397
398 return pCache;
399}
400
402 DocumentKey const & pDocument,
403 const Size& rPreviewSize,
404 const std::shared_ptr<BitmapCache>& rpCache)
405{
406 // Look up the list of recently used caches for the given document.
408 if (iQueue == mpRecentlyUsedPageCaches->end())
409 iQueue = mpRecentlyUsedPageCaches->emplace(
410 pDocument, RecentlyUsedQueue()
411 ).first;
412
413 if (iQueue != mpRecentlyUsedPageCaches->end())
414 {
415 iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache));
416 // Shorten the list of recently used caches to the allowed maximal length.
417 while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
418 iQueue->second.pop_back();
419 }
420}
421
422} // end of namespace ::sd::slidesorter::cache
423
424/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Compare entries in the cache container with respect to the cache address only.
bool operator()(const PageCacheContainer::value_type &rValue) const
The recently used caches are stored in one queue for each document.
Provide and manage the preview bitmap caches for all slide sorter instances.
static std::shared_ptr< PageCacheManager > Instance()
Return the one instance of the PageCacheManager class.
std::shared_ptr< BitmapCache > GetRecentlyUsedCache(const DocumentKey &pDocument, const Size &rSize)
std::shared_ptr< BitmapCache > GetCache(const DocumentKey &pDocument, const Size &rPreviewSize)
Look up the cache for the given model in which the previews have the specified size.
css::uno::Reference< css::uno::XInterface > DocumentKey
static const sal_uInt32 mnMaximalRecentlyCacheCount
The maximal number of recently used caches that are kept alive after they have become inactive,...
std::unique_ptr< RecentlyUsedPageCaches > mpRecentlyUsedPageCaches
void ReleaseCache(const std::shared_ptr< BitmapCache > &rpCache)
Tell the cache manager to release its own reference to the specified cache.
std::vector< std::pair< Size, std::shared_ptr< BitmapCache > > > BestFittingPageCaches
void InvalidateAllCaches()
Invalidate all the caches that are currently in use and destroy those that are not.
void Recycle(const std::shared_ptr< BitmapCache > &rpCache, const DocumentKey &pDocument, const Size &rPreviewSize)
This method is used internally to initialize a newly created BitmapCache with already existing previe...
void ReleasePreviewBitmap(const SdrPage *pPage)
Call this method when a page has been deleted and its preview is not needed anymore.
void InvalidateAllPreviewBitmaps(const DocumentKey &pDocument)
Invalidate the preview bitmaps for all slides that belong to the specified document.
bool InvalidatePreviewBitmap(const DocumentKey &pDocument, const SdrPage *pPage)
Invalidate the preview bitmap for one slide that belongs to the specified document.
std::shared_ptr< BitmapCache > ChangeSize(const std::shared_ptr< BitmapCache > &rpCache, const Size &rOldPreviewSize, const Size &rNewPreviewSize)
This is an information to the cache manager that the size of preview bitmaps in the specified cache h...
std::unique_ptr< PageCacheContainer > mpPageCaches
void PutRecentlyUsedCache(DocumentKey const &pDocument, const Size &rPreviewSize, const std::shared_ptr< BitmapCache > &rpCache)
Add the given cache to the list of recently used caches for the document.
static std::weak_ptr< PageCacheManager > mpInstance
Singleton instance of the cache manager.
EmbeddedObjectRef * pObject
args