bump product version to 5.0.4.1
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsBitmapCache.cxx
blobeabd8aeb017647df113cf2ede44967eb12322771
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 "SlsBitmapCache.hxx"
21 #include "SlsCacheCompactor.hxx"
22 #include "SlsBitmapCompressor.hxx"
23 #include "SlsCacheConfiguration.hxx"
25 #include "sdpage.hxx"
26 #include "drawdoc.hxx"
28 // Define the default value for the maximal cache size that is used for
29 // previews that are currently not visible. The visible previews are all
30 // held in memory at all times. This default is used only when the
31 // configuration does not have a value.
32 static const sal_Int32 MAXIMAL_CACHE_SIZE = 4L*1024L*1024L;
34 using namespace ::com::sun::star::uno;
36 namespace sd { namespace slidesorter { namespace cache {
38 class BitmapCache::CacheEntry
40 public:
41 CacheEntry(const Bitmap& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious);
42 CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious);
43 ~CacheEntry() {};
44 inline void Recycle (const CacheEntry& rEntry);
45 inline sal_Int32 GetMemorySize() const;
46 void Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor);
47 inline void Decompress();
49 bool IsUpToDate() const { return mbIsUpToDate; }
50 void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; }
51 sal_Int32 GetAccessTime() const { return mnLastAccessTime; }
52 void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; }
54 Bitmap GetPreview() const { return maPreview; }
55 inline void SetPreview (const Bitmap& rPreview);
56 bool HasPreview() const;
58 Bitmap GetMarkedPreview() const { return maMarkedPreview; }
59 inline void SetMarkedPreview (const Bitmap& rMarkePreview);
61 bool HasReplacement() const { return (mpReplacement.get() != NULL); }
62 inline bool HasLosslessReplacement() const;
63 void Invalidate() { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; }
64 bool IsPrecious() const { return mbIsPrecious; }
65 void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; }
67 private:
68 Bitmap maPreview;
69 Bitmap maMarkedPreview;
70 ::boost::shared_ptr<BitmapReplacement> mpReplacement;
71 ::boost::shared_ptr<BitmapCompressor> mpCompressor;
72 Size maBitmapSize;
73 bool mbIsUpToDate;
74 sal_Int32 mnLastAccessTime;
75 // When this flag is set then the bitmap is not modified by a cache
76 // compactor.
77 bool mbIsPrecious;
79 class CacheEntry;
81 class CacheHash {
82 public:
83 size_t operator()(const BitmapCache::CacheKey& p) const
84 { return reinterpret_cast<size_t>(p); }
87 class BitmapCache::CacheBitmapContainer
88 : public std::unordered_map<CacheKey, CacheEntry, CacheHash>
90 public:
91 CacheBitmapContainer() {}
94 namespace {
96 typedef ::std::vector<
97 ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey,
98 ::sd::slidesorter::cache::BitmapCache::CacheEntry>
99 > SortableBitmapContainer;
101 /** Compare elements of the bitmap cache according to their last access
102 time.
104 class AccessTimeComparator
106 public:
107 bool operator () (
108 const SortableBitmapContainer::value_type& e1,
109 const SortableBitmapContainer::value_type& e2)
111 return e1.second.GetAccessTime() < e2.second.GetAccessTime();
115 } // end of anonymous namespace
117 //===== BitmapCache =========================================================
119 BitmapCache::BitmapCache (const sal_Int32 nMaximalNormalCacheSize)
120 : maMutex(),
121 mpBitmapContainer(new CacheBitmapContainer()),
122 mnNormalCacheSize(0),
123 mnPreciousCacheSize(0),
124 mnCurrentAccessTime(0),
125 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
126 mpCacheCompactor(),
127 mbIsFull(false)
129 if (nMaximalNormalCacheSize > 0)
130 mnMaximalNormalCacheSize = nMaximalNormalCacheSize;
131 else
133 Any aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize"));
134 if (aCacheSize.has<sal_Int32>())
135 aCacheSize >>= mnMaximalNormalCacheSize;
138 mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize);
141 BitmapCache::~BitmapCache()
143 Clear();
146 void BitmapCache::Clear()
148 ::osl::MutexGuard aGuard (maMutex);
150 mpBitmapContainer->clear();
151 mnNormalCacheSize = 0;
152 mnPreciousCacheSize = 0;
153 mnCurrentAccessTime = 0;
156 bool BitmapCache::HasBitmap (const CacheKey& rKey)
158 ::osl::MutexGuard aGuard (maMutex);
160 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
161 return (iEntry != mpBitmapContainer->end()
162 && (iEntry->second.HasPreview() || iEntry->second.HasReplacement()));
165 bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey)
167 ::osl::MutexGuard aGuard (maMutex);
169 bool bIsUpToDate = false;
170 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
171 if (aIterator != mpBitmapContainer->end())
172 bIsUpToDate = aIterator->second.IsUpToDate();
174 return bIsUpToDate;
177 Bitmap BitmapCache::GetBitmap (const CacheKey& rKey)
179 ::osl::MutexGuard aGuard (maMutex);
181 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
182 if (iEntry == mpBitmapContainer->end())
184 // Create an empty bitmap for the given key that acts as placeholder
185 // until we are given the real one. Mark it as not being up to date.
186 SetBitmap(rKey, Bitmap(), false);
187 iEntry = mpBitmapContainer->find(rKey);
188 iEntry->second.SetUpToDate(false);
190 else
192 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
194 // Maybe we have to decompress the preview.
195 if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement())
197 UpdateCacheSize(iEntry->second, REMOVE);
198 iEntry->second.Decompress();
199 UpdateCacheSize(iEntry->second, ADD);
202 return iEntry->second.GetPreview();
205 Bitmap BitmapCache::GetMarkedBitmap (const CacheKey& rKey)
207 ::osl::MutexGuard aGuard (maMutex);
209 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
210 if (iEntry != mpBitmapContainer->end())
212 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
213 return iEntry->second.GetMarkedPreview();
215 else
216 return Bitmap();
219 void BitmapCache::ReleaseBitmap (const CacheKey& rKey)
221 ::osl::MutexGuard aGuard (maMutex);
223 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
224 if (aIterator != mpBitmapContainer->end())
226 UpdateCacheSize(aIterator->second, REMOVE);
227 mpBitmapContainer->erase(aIterator);
231 bool BitmapCache::InvalidateBitmap (const CacheKey& rKey)
233 ::osl::MutexGuard aGuard (maMutex);
235 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
236 if (iEntry != mpBitmapContainer->end())
238 iEntry->second.SetUpToDate(false);
240 // When there is a preview then we release the replacement. The
241 // preview itself is kept until a new one is created.
242 if (iEntry->second.HasPreview())
244 UpdateCacheSize(iEntry->second, REMOVE);
245 iEntry->second.Invalidate();
246 UpdateCacheSize(iEntry->second, ADD);
248 return true;
250 else
251 return false;
254 void BitmapCache::InvalidateCache()
256 ::osl::MutexGuard aGuard (maMutex);
258 CacheBitmapContainer::iterator iEntry;
259 for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry)
261 iEntry->second.Invalidate();
263 ReCalculateTotalCacheSize();
266 void BitmapCache::SetBitmap (
267 const CacheKey& rKey,
268 const Bitmap& rPreview,
269 bool bIsPrecious)
271 ::osl::MutexGuard aGuard (maMutex);
273 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
274 if (iEntry != mpBitmapContainer->end())
276 UpdateCacheSize(iEntry->second, REMOVE);
277 iEntry->second.SetPreview(rPreview);
278 iEntry->second.SetUpToDate(true);
279 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
281 else
283 iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
284 rKey,
285 CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious))
286 ).first;
289 if (iEntry != mpBitmapContainer->end())
290 UpdateCacheSize(iEntry->second, ADD);
293 void BitmapCache::SetMarkedBitmap (
294 const CacheKey& rKey,
295 const Bitmap& rPreview)
297 ::osl::MutexGuard aGuard (maMutex);
299 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
300 if (iEntry != mpBitmapContainer->end())
302 UpdateCacheSize(iEntry->second, REMOVE);
303 iEntry->second.SetMarkedPreview(rPreview);
304 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
305 UpdateCacheSize(iEntry->second, ADD);
309 void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious)
311 ::osl::MutexGuard aGuard (maMutex);
313 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
314 if (iEntry != mpBitmapContainer->end())
316 if (iEntry->second.IsPrecious() != bIsPrecious)
318 UpdateCacheSize(iEntry->second, REMOVE);
319 iEntry->second.SetPrecious(bIsPrecious);
320 UpdateCacheSize(iEntry->second, ADD);
323 else if (bIsPrecious)
325 iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
326 rKey,
327 CacheEntry(Bitmap(), mnCurrentAccessTime++, bIsPrecious))
328 ).first;
329 UpdateCacheSize(iEntry->second, ADD);
333 void BitmapCache::ReCalculateTotalCacheSize()
335 ::osl::MutexGuard aGuard (maMutex);
337 mnNormalCacheSize = 0;
338 mnPreciousCacheSize = 0;
339 CacheBitmapContainer::iterator iEntry;
340 for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry)
342 if (iEntry->second.IsPrecious())
343 mnPreciousCacheSize += iEntry->second.GetMemorySize();
344 else
345 mnNormalCacheSize += iEntry->second.GetMemorySize();
347 mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize);
349 SAL_INFO("sd.sls", OSL_THIS_FUNC << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize);
352 void BitmapCache::Recycle (const BitmapCache& rCache)
354 ::osl::MutexGuard aGuard (maMutex);
356 CacheBitmapContainer::const_iterator iOtherEntry;
357 for (iOtherEntry=rCache.mpBitmapContainer->begin();
358 iOtherEntry!=rCache.mpBitmapContainer->end();
359 ++iOtherEntry)
361 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(iOtherEntry->first));
362 if (iEntry == mpBitmapContainer->end())
364 iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
365 iOtherEntry->first,
366 CacheEntry(mnCurrentAccessTime++, true))
367 ).first;
368 UpdateCacheSize(iEntry->second, ADD);
370 if (iEntry != mpBitmapContainer->end())
372 UpdateCacheSize(iEntry->second, REMOVE);
373 iEntry->second.Recycle(iOtherEntry->second);
374 UpdateCacheSize(iEntry->second, ADD);
379 ::std::unique_ptr<BitmapCache::CacheIndex> BitmapCache::GetCacheIndex (
380 bool bIncludePrecious,
381 bool bIncludeNoPreview) const
383 ::osl::MutexGuard aGuard (maMutex);
385 // Create a copy of the bitmap container.
386 SortableBitmapContainer aSortedContainer;
387 aSortedContainer.reserve(mpBitmapContainer->size());
389 // Copy the relevant entries.
390 CacheBitmapContainer::iterator iEntry;
391 for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry)
393 if ( ! bIncludePrecious && iEntry->second.IsPrecious())
394 continue;
396 if ( ! bIncludeNoPreview && ! iEntry->second.HasPreview())
397 continue;
399 aSortedContainer.push_back(SortableBitmapContainer::value_type(
400 iEntry->first,iEntry->second));
403 // Sort the remaining entries.
404 ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());
406 // Return a list with the keys of the sorted entries.
407 ::std::unique_ptr<CacheIndex> pIndex(new CacheIndex());
408 SortableBitmapContainer::iterator iIndexEntry;
409 pIndex->reserve(aSortedContainer.size());
410 for (iIndexEntry=aSortedContainer.begin(); iIndexEntry!=aSortedContainer.end(); ++iIndexEntry)
411 pIndex->push_back(iIndexEntry->first);
412 return pIndex;
415 void BitmapCache::Compress (
416 const CacheKey& rKey,
417 const ::boost::shared_ptr<BitmapCompressor>& rpCompressor)
419 ::osl::MutexGuard aGuard (maMutex);
421 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
422 if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview())
424 UpdateCacheSize(iEntry->second, REMOVE);
425 iEntry->second.Compress(rpCompressor);
426 UpdateCacheSize(iEntry->second, ADD);
430 void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation)
432 sal_Int32 nEntrySize (rEntry.GetMemorySize());
433 sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize);
434 switch (eOperation)
436 case ADD:
437 rCacheSize += nEntrySize;
438 if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize)
440 mbIsFull = true;
441 SAL_INFO("sd.sls", OSL_THIS_FUNC << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize);
442 mpCacheCompactor->RequestCompaction();
444 break;
446 case REMOVE:
447 rCacheSize -= nEntrySize;
448 if (mnNormalCacheSize < mnMaximalNormalCacheSize)
449 mbIsFull = false;
450 break;
452 default:
453 OSL_ASSERT(false);
454 break;
458 //===== CacheEntry ============================================================
460 BitmapCache::CacheEntry::CacheEntry(
461 sal_Int32 nLastAccessTime,
462 bool bIsPrecious)
463 : maPreview(),
464 maMarkedPreview(),
465 mbIsUpToDate(true),
466 mnLastAccessTime(nLastAccessTime),
467 mbIsPrecious(bIsPrecious)
471 BitmapCache::CacheEntry::CacheEntry(
472 const Bitmap& rPreview,
473 sal_Int32 nLastAccessTime,
474 bool bIsPrecious)
475 : maPreview(rPreview),
476 maMarkedPreview(),
477 mbIsUpToDate(true),
478 mnLastAccessTime(nLastAccessTime),
479 mbIsPrecious(bIsPrecious)
483 inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry)
485 if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
486 && ! (HasPreview() || HasLosslessReplacement()))
488 maPreview = rEntry.maPreview;
489 maMarkedPreview = rEntry.maMarkedPreview;
490 mpReplacement = rEntry.mpReplacement;
491 mpCompressor = rEntry.mpCompressor;
492 mnLastAccessTime = rEntry.mnLastAccessTime;
493 mbIsUpToDate = rEntry.mbIsUpToDate;
497 inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize() const
499 sal_Int32 nSize (0);
500 nSize += maPreview.GetSizeBytes();
501 nSize += maMarkedPreview.GetSizeBytes();
502 if (mpReplacement.get() != NULL)
503 nSize += mpReplacement->GetMemorySize();
504 return nSize;
507 void BitmapCache::CacheEntry::Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor)
509 if ( ! maPreview.IsEmpty())
511 if (mpReplacement.get() == NULL)
513 mpReplacement = rpCompressor->Compress(maPreview);
515 #if OSL_DEBUG_LEVEL > 2
516 sal_uInt32 nOldSize (maPreview.GetSizeBytes());
517 sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0);
518 if (nOldSize == 0)
519 nOldSize = 1;
520 sal_Int32 nRatio (100L * nNewSize / nOldSize);
521 SAL_INFO("sd.sls", OSL_THIS_FUNC << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)");
522 #endif
524 mpCompressor = rpCompressor;
527 maPreview.SetEmpty();
528 maMarkedPreview.SetEmpty();
532 inline void BitmapCache::CacheEntry::Decompress()
534 if (mpReplacement.get()!=NULL && mpCompressor.get()!=NULL && maPreview.IsEmpty())
536 maPreview = mpCompressor->Decompress(*mpReplacement);
537 maMarkedPreview.SetEmpty();
538 if ( ! mpCompressor->IsLossless())
539 mbIsUpToDate = false;
543 inline void BitmapCache::CacheEntry::SetPreview (const Bitmap& rPreview)
545 maPreview = rPreview;
546 maMarkedPreview.SetEmpty();
547 mpReplacement.reset();
548 mpCompressor.reset();
551 bool BitmapCache::CacheEntry::HasPreview() const
553 return ! maPreview.IsEmpty();
556 inline void BitmapCache::CacheEntry::SetMarkedPreview (const Bitmap& rMarkedPreview)
558 maMarkedPreview = rMarkedPreview;
561 inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const
563 return mpReplacement.get()!=NULL
564 && mpCompressor.get()!=NULL
565 && mpCompressor->IsLossless();
568 } } } // end of namespace ::sd::slidesorter::cache
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */