1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
32 /** Collection of data that is stored for all active preview caches.
37 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument
;
38 Size
const maPreviewSize
;
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
;
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();
57 /** Collection of data that is stored for the inactive, recently used
60 class RecentlyUsedCacheDescriptor
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
83 class BestFittingCacheComparer
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
)
94 else if (rElement1
.first
== maPreferredSize
)
97 return (rElement1
.first
.Width()*rElement1
.first
.Height()
98 > rElement2
.first
.Width()*rElement2
.first
.Height());
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
>
118 PageCacheContainer() {}
120 /** Compare entries in the cache container with respect to the cache
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
; }
129 std::shared_ptr
<BitmapCache
> mpCache
;
133 /** The recently used caches are stored in one queue for each document.
135 class PageCacheManager::RecentlyUsedPageCaches
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
;
143 std::map
<key_type
,mapped_type
> maMap
;
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
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
;
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
);
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(),
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
,
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(),
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
293 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey
aKey (
294 iCacheToChange
->first
.mpDocument
);
295 mpPageCaches
->erase(iCacheToChange
);
296 mpPageCaches
->emplace(
297 CacheDescriptor(aKey
,rNewPreviewSize
),
304 assert(iCacheToChange
!= mpPageCaches
->end());
311 bool PageCacheManager::InvalidatePreviewBitmap (
312 const DocumentKey
& pDocument
,
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
);
339 void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey
& pDocument
)
341 if (pDocument
== nullptr)
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
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
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
);
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()
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: */