Bump version to 21.06.18.1
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsBitmapCache.cxx
blob025f6251afaf63fc2fa177723a24958f976f4382
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 <memory>
21 #include <unordered_map>
22 #include "SlsBitmapCache.hxx"
23 #include "SlsCacheCompactor.hxx"
24 #include "SlsBitmapCompressor.hxx"
25 #include "SlsCacheConfiguration.hxx"
27 #include <osl/diagnose.h>
28 #include <sal/log.hxx>
30 // Define the default value for the maximal cache size that is used for
31 // previews that are currently not visible. The visible previews are all
32 // held in memory at all times. This default is used only when the
33 // configuration does not have a value.
34 const sal_Int32 MAXIMAL_CACHE_SIZE = 4*1024L*1024L;
36 using namespace ::com::sun::star::uno;
38 namespace sd::slidesorter::cache {
40 class BitmapCache::CacheEntry
42 public:
43 CacheEntry(const BitmapEx& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious);
44 CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious);
45 inline void Recycle (const CacheEntry& rEntry);
46 inline sal_Int32 GetMemorySize() const;
47 void Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor);
48 inline void Decompress();
50 bool IsUpToDate() const { return mbIsUpToDate; }
51 void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; }
52 sal_Int32 GetAccessTime() const { return mnLastAccessTime; }
53 void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; }
55 const BitmapEx& GetPreview() const { return maPreview; }
56 inline void SetPreview (const BitmapEx& rPreview);
57 bool HasPreview() const;
59 const BitmapEx& GetMarkedPreview() const { return maMarkedPreview; }
60 inline void SetMarkedPreview (const BitmapEx& rMarkePreview);
62 bool HasReplacement() const { return (mpReplacement != nullptr); }
63 inline bool HasLosslessReplacement() const;
64 void Invalidate() { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; }
65 bool IsPrecious() const { return mbIsPrecious; }
66 void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; }
68 private:
69 BitmapEx maPreview;
70 BitmapEx maMarkedPreview;
71 std::shared_ptr<BitmapReplacement> mpReplacement;
72 std::shared_ptr<BitmapCompressor> mpCompressor;
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;
80 namespace {
82 class CacheHash {
83 public:
84 size_t operator()(const BitmapCache::CacheKey& p) const
85 { return reinterpret_cast<size_t>(p); }
90 class BitmapCache::CacheBitmapContainer
91 : public std::unordered_map<CacheKey, CacheEntry, CacheHash>
93 public:
94 CacheBitmapContainer() {}
97 namespace {
99 typedef ::std::vector<
100 ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey,
101 ::sd::slidesorter::cache::BitmapCache::CacheEntry>
102 > SortableBitmapContainer;
104 /** Compare elements of the bitmap cache according to their last access
105 time.
107 class AccessTimeComparator
109 public:
110 bool operator () (
111 const SortableBitmapContainer::value_type& e1,
112 const SortableBitmapContainer::value_type& e2)
114 return e1.second.GetAccessTime() < e2.second.GetAccessTime();
118 } // end of anonymous namespace
120 //===== BitmapCache =========================================================
122 BitmapCache::BitmapCache ()
123 : maMutex(),
124 mpBitmapContainer(new CacheBitmapContainer()),
125 mnNormalCacheSize(0),
126 mnPreciousCacheSize(0),
127 mnCurrentAccessTime(0),
128 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
129 mpCacheCompactor(),
130 mbIsFull(false)
132 Any aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize"));
133 if (aCacheSize.has<sal_Int32>())
134 aCacheSize >>= mnMaximalNormalCacheSize;
136 mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize);
139 BitmapCache::~BitmapCache()
141 Clear();
144 void BitmapCache::Clear()
146 ::osl::MutexGuard aGuard (maMutex);
148 mpBitmapContainer->clear();
149 mnNormalCacheSize = 0;
150 mnPreciousCacheSize = 0;
151 mnCurrentAccessTime = 0;
154 bool BitmapCache::HasBitmap (const CacheKey& rKey)
156 ::osl::MutexGuard aGuard (maMutex);
158 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
159 return (iEntry != mpBitmapContainer->end()
160 && (iEntry->second.HasPreview() || iEntry->second.HasReplacement()));
163 bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey)
165 ::osl::MutexGuard aGuard (maMutex);
167 bool bIsUpToDate = false;
168 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
169 if (aIterator != mpBitmapContainer->end())
170 bIsUpToDate = aIterator->second.IsUpToDate();
172 return bIsUpToDate;
175 BitmapEx BitmapCache::GetBitmap (const CacheKey& rKey)
177 ::osl::MutexGuard aGuard (maMutex);
179 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
180 if (iEntry == mpBitmapContainer->end())
182 // Create an empty bitmap for the given key that acts as placeholder
183 // until we are given the real one. Mark it as not being up to date.
184 SetBitmap(rKey, BitmapEx(), false);
185 iEntry = mpBitmapContainer->find(rKey);
186 iEntry->second.SetUpToDate(false);
188 else
190 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
192 // Maybe we have to decompress the preview.
193 if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement())
195 UpdateCacheSize(iEntry->second, REMOVE);
196 iEntry->second.Decompress();
197 UpdateCacheSize(iEntry->second, ADD);
200 return iEntry->second.GetPreview();
203 BitmapEx BitmapCache::GetMarkedBitmap (const CacheKey& rKey)
205 ::osl::MutexGuard aGuard (maMutex);
207 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
208 if (iEntry != mpBitmapContainer->end())
210 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
211 return iEntry->second.GetMarkedPreview();
213 else
214 return BitmapEx();
217 void BitmapCache::ReleaseBitmap (const CacheKey& rKey)
219 ::osl::MutexGuard aGuard (maMutex);
221 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
222 if (aIterator != mpBitmapContainer->end())
224 UpdateCacheSize(aIterator->second, REMOVE);
225 mpBitmapContainer->erase(aIterator);
229 bool BitmapCache::InvalidateBitmap (const CacheKey& rKey)
231 ::osl::MutexGuard aGuard (maMutex);
233 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
234 if (iEntry != mpBitmapContainer->end())
236 iEntry->second.SetUpToDate(false);
238 // When there is a preview then we release the replacement. The
239 // preview itself is kept until a new one is created.
240 if (iEntry->second.HasPreview())
242 UpdateCacheSize(iEntry->second, REMOVE);
243 iEntry->second.Invalidate();
244 UpdateCacheSize(iEntry->second, ADD);
246 return true;
248 else
249 return false;
252 void BitmapCache::InvalidateCache()
254 ::osl::MutexGuard aGuard (maMutex);
256 for (auto& rEntry : *mpBitmapContainer)
258 rEntry.second.Invalidate();
260 ReCalculateTotalCacheSize();
263 void BitmapCache::SetBitmap (
264 const CacheKey& rKey,
265 const BitmapEx& rPreview,
266 bool bIsPrecious)
268 ::osl::MutexGuard aGuard (maMutex);
270 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
271 if (iEntry != mpBitmapContainer->end())
273 UpdateCacheSize(iEntry->second, REMOVE);
274 iEntry->second.SetPreview(rPreview);
275 iEntry->second.SetUpToDate(true);
276 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
278 else
280 iEntry = mpBitmapContainer->emplace(
281 rKey,
282 CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious)
283 ).first;
286 if (iEntry != mpBitmapContainer->end())
287 UpdateCacheSize(iEntry->second, ADD);
290 void BitmapCache::SetMarkedBitmap (
291 const CacheKey& rKey,
292 const BitmapEx& rPreview)
294 ::osl::MutexGuard aGuard (maMutex);
296 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
297 if (iEntry != mpBitmapContainer->end())
299 UpdateCacheSize(iEntry->second, REMOVE);
300 iEntry->second.SetMarkedPreview(rPreview);
301 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
302 UpdateCacheSize(iEntry->second, ADD);
306 void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious)
308 ::osl::MutexGuard aGuard (maMutex);
310 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
311 if (iEntry != mpBitmapContainer->end())
313 if (iEntry->second.IsPrecious() != bIsPrecious)
315 UpdateCacheSize(iEntry->second, REMOVE);
316 iEntry->second.SetPrecious(bIsPrecious);
317 UpdateCacheSize(iEntry->second, ADD);
320 else if (bIsPrecious)
322 iEntry = mpBitmapContainer->emplace(
323 rKey,
324 CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious)
325 ).first;
326 UpdateCacheSize(iEntry->second, ADD);
330 void BitmapCache::ReCalculateTotalCacheSize()
332 ::osl::MutexGuard aGuard (maMutex);
334 mnNormalCacheSize = 0;
335 mnPreciousCacheSize = 0;
336 for (const auto& rEntry : *mpBitmapContainer)
338 if (rEntry.second.IsPrecious())
339 mnPreciousCacheSize += rEntry.second.GetMemorySize();
340 else
341 mnNormalCacheSize += rEntry.second.GetMemorySize();
343 mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize);
345 SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize);
348 void BitmapCache::Recycle (const BitmapCache& rCache)
350 ::osl::MutexGuard aGuard (maMutex);
352 for (const auto& rOtherEntry : *rCache.mpBitmapContainer)
354 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first));
355 if (iEntry == mpBitmapContainer->end())
357 iEntry = mpBitmapContainer->emplace(
358 rOtherEntry.first,
359 CacheEntry(mnCurrentAccessTime++, true)
360 ).first;
361 UpdateCacheSize(iEntry->second, ADD);
363 if (iEntry != mpBitmapContainer->end())
365 UpdateCacheSize(iEntry->second, REMOVE);
366 iEntry->second.Recycle(rOtherEntry.second);
367 UpdateCacheSize(iEntry->second, ADD);
372 ::std::unique_ptr<BitmapCache::CacheIndex> BitmapCache::GetCacheIndex() const
374 ::osl::MutexGuard aGuard (maMutex);
376 // Create a copy of the bitmap container.
377 SortableBitmapContainer aSortedContainer;
378 aSortedContainer.reserve(mpBitmapContainer->size());
380 // Copy the relevant entries.
381 for (const auto& rEntry : *mpBitmapContainer)
383 if ( rEntry.second.IsPrecious())
384 continue;
386 if ( ! rEntry.second.HasPreview())
387 continue;
389 aSortedContainer.emplace_back(rEntry.first, rEntry.second);
392 // Sort the remaining entries.
393 ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());
395 // Return a list with the keys of the sorted entries.
396 ::std::unique_ptr<CacheIndex> pIndex(new CacheIndex);
397 pIndex->reserve(aSortedContainer.size());
398 for (const auto& rIndexEntry : aSortedContainer)
399 pIndex->push_back(rIndexEntry.first);
400 return pIndex;
403 void BitmapCache::Compress (
404 const CacheKey& rKey,
405 const std::shared_ptr<BitmapCompressor>& rpCompressor)
407 ::osl::MutexGuard aGuard (maMutex);
409 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
410 if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview())
412 UpdateCacheSize(iEntry->second, REMOVE);
413 iEntry->second.Compress(rpCompressor);
414 UpdateCacheSize(iEntry->second, ADD);
418 void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation)
420 sal_Int32 nEntrySize (rEntry.GetMemorySize());
421 sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize);
422 switch (eOperation)
424 case ADD:
425 rCacheSize += nEntrySize;
426 if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize)
428 mbIsFull = true;
429 SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize);
430 mpCacheCompactor->RequestCompaction();
432 break;
434 case REMOVE:
435 rCacheSize -= nEntrySize;
436 if (mnNormalCacheSize < mnMaximalNormalCacheSize)
437 mbIsFull = false;
438 break;
440 default:
441 assert(false);
442 break;
446 //===== CacheEntry ============================================================
448 BitmapCache::CacheEntry::CacheEntry(
449 sal_Int32 nLastAccessTime,
450 bool bIsPrecious)
451 : maPreview(),
452 maMarkedPreview(),
453 mbIsUpToDate(true),
454 mnLastAccessTime(nLastAccessTime),
455 mbIsPrecious(bIsPrecious)
459 BitmapCache::CacheEntry::CacheEntry(
460 const BitmapEx& rPreview,
461 sal_Int32 nLastAccessTime,
462 bool bIsPrecious)
463 : maPreview(rPreview),
464 maMarkedPreview(),
465 mbIsUpToDate(true),
466 mnLastAccessTime(nLastAccessTime),
467 mbIsPrecious(bIsPrecious)
471 inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry)
473 if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
474 && ! (HasPreview() || HasLosslessReplacement()))
476 maPreview = rEntry.maPreview;
477 maMarkedPreview = rEntry.maMarkedPreview;
478 mpReplacement = rEntry.mpReplacement;
479 mpCompressor = rEntry.mpCompressor;
480 mnLastAccessTime = rEntry.mnLastAccessTime;
481 mbIsUpToDate = rEntry.mbIsUpToDate;
485 inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize() const
487 sal_Int32 nSize (0);
488 nSize += maPreview.GetSizeBytes();
489 nSize += maMarkedPreview.GetSizeBytes();
490 if (mpReplacement != nullptr)
491 nSize += mpReplacement->GetMemorySize();
492 return nSize;
495 void BitmapCache::CacheEntry::Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor)
497 if ( maPreview.IsEmpty())
498 return;
500 if (mpReplacement == nullptr)
502 mpReplacement = rpCompressor->Compress(maPreview);
504 #ifdef DEBUG_SD_SLSBITMAPCACHE
505 sal_uInt32 nOldSize (maPreview.GetSizeBytes());
506 sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0);
507 if (nOldSize == 0)
508 nOldSize = 1;
509 sal_Int32 nRatio (100L * nNewSize / nOldSize);
510 SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)");
511 #endif
513 mpCompressor = rpCompressor;
516 maPreview.SetEmpty();
517 maMarkedPreview.SetEmpty();
520 inline void BitmapCache::CacheEntry::Decompress()
522 if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty())
524 maPreview = mpCompressor->Decompress(*mpReplacement);
525 maMarkedPreview.SetEmpty();
526 if ( ! mpCompressor->IsLossless())
527 mbIsUpToDate = false;
531 inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx& rPreview)
533 maPreview = rPreview;
534 maMarkedPreview.SetEmpty();
535 mpReplacement.reset();
536 mpCompressor.reset();
539 bool BitmapCache::CacheEntry::HasPreview() const
541 return ! maPreview.IsEmpty();
544 inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview)
546 maMarkedPreview = rMarkedPreview;
549 inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const
551 return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless();
554 } // end of namespace ::sd::slidesorter::cache
556 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */