tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsBitmapCache.cxx
blobca0bd8cf4b8b2e44ae331fd87b61d02d2032dd3b
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 <officecfg/Office/Impress.hxx>
22 #include <memory>
23 #include <unordered_map>
24 #include "SlsBitmapCache.hxx"
25 #include "SlsCacheCompactor.hxx"
26 #include "SlsBitmapCompressor.hxx"
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 : mpBitmapContainer(new CacheBitmapContainer()),
124 mnNormalCacheSize(0),
125 mnPreciousCacheSize(0),
126 mnCurrentAccessTime(0),
127 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
128 mbIsFull(false)
130 mnMaximalNormalCacheSize = officecfg::Office::Impress::MultiPaneGUI::SlideSorterBar::PreviewCache::CacheSize::get();
132 mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize);
135 BitmapCache::~BitmapCache()
137 Clear();
140 void BitmapCache::Clear()
142 std::unique_lock aGuard (maMutex);
144 mpBitmapContainer->clear();
145 mnNormalCacheSize = 0;
146 mnPreciousCacheSize = 0;
147 mnCurrentAccessTime = 0;
150 bool BitmapCache::HasBitmap (const CacheKey& rKey)
152 std::unique_lock aGuard (maMutex);
154 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
155 return (iEntry != mpBitmapContainer->end()
156 && (iEntry->second.HasPreview() || iEntry->second.HasReplacement()));
159 bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey)
161 std::unique_lock aGuard (maMutex);
163 bool bIsUpToDate = false;
164 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
165 if (aIterator != mpBitmapContainer->end())
166 bIsUpToDate = aIterator->second.IsUpToDate();
168 return bIsUpToDate;
171 BitmapEx BitmapCache::GetBitmap (const CacheKey& rKey)
173 std::unique_lock aGuard (maMutex);
175 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
176 if (iEntry == mpBitmapContainer->end())
178 // Create an empty bitmap for the given key that acts as placeholder
179 // until we are given the real one. Mark it as not being up to date.
180 SetBitmap(aGuard, rKey, BitmapEx(), false);
181 iEntry = mpBitmapContainer->find(rKey);
182 iEntry->second.SetUpToDate(false);
184 else
186 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
188 // Maybe we have to decompress the preview.
189 if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement())
191 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
192 iEntry->second.Decompress();
193 UpdateCacheSize(aGuard, iEntry->second, ADD);
196 return iEntry->second.GetPreview();
199 BitmapEx BitmapCache::GetMarkedBitmap (const CacheKey& rKey)
201 std::unique_lock aGuard (maMutex);
203 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
204 if (iEntry != mpBitmapContainer->end())
206 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
207 return iEntry->second.GetMarkedPreview();
209 else
210 return BitmapEx();
213 void BitmapCache::ReleaseBitmap (const CacheKey& rKey)
215 std::unique_lock aGuard (maMutex);
217 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
218 if (aIterator != mpBitmapContainer->end())
220 UpdateCacheSize(aGuard, aIterator->second, REMOVE);
221 mpBitmapContainer->erase(aIterator);
225 bool BitmapCache::InvalidateBitmap (const CacheKey& rKey)
227 std::unique_lock aGuard (maMutex);
229 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
230 if (iEntry != mpBitmapContainer->end())
232 iEntry->second.SetUpToDate(false);
234 // When there is a preview then we release the replacement. The
235 // preview itself is kept until a new one is created.
236 if (iEntry->second.HasPreview())
238 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
239 iEntry->second.Invalidate();
240 UpdateCacheSize(aGuard, iEntry->second, ADD);
242 return true;
244 else
245 return false;
248 void BitmapCache::InvalidateCache()
250 std::unique_lock aGuard (maMutex);
252 for (auto& rEntry : *mpBitmapContainer)
254 rEntry.second.Invalidate();
256 ReCalculateTotalCacheSize(aGuard);
259 void BitmapCache::SetBitmap (
260 const CacheKey& rKey,
261 const BitmapEx& rPreview,
262 bool bIsPrecious)
264 std::unique_lock aGuard (maMutex);
265 SetBitmap(aGuard, rKey, rPreview, bIsPrecious);
268 void BitmapCache::SetBitmap (
269 std::unique_lock<std::mutex>& rGuard,
270 const CacheKey& rKey,
271 const BitmapEx& rPreview,
272 bool bIsPrecious)
274 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
275 if (iEntry != mpBitmapContainer->end())
277 UpdateCacheSize(rGuard, iEntry->second, REMOVE);
278 iEntry->second.SetPreview(rPreview);
279 iEntry->second.SetUpToDate(true);
280 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
282 else
284 iEntry = mpBitmapContainer->emplace(
285 rKey,
286 CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious)
287 ).first;
290 if (iEntry != mpBitmapContainer->end())
291 UpdateCacheSize(rGuard, iEntry->second, ADD);
294 void BitmapCache::SetMarkedBitmap (
295 const CacheKey& rKey,
296 const BitmapEx& rPreview)
298 std::unique_lock aGuard (maMutex);
300 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
301 if (iEntry != mpBitmapContainer->end())
303 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
304 iEntry->second.SetMarkedPreview(rPreview);
305 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
306 UpdateCacheSize(aGuard, iEntry->second, ADD);
310 void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious)
312 std::unique_lock aGuard (maMutex);
314 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
315 if (iEntry != mpBitmapContainer->end())
317 if (iEntry->second.IsPrecious() != bIsPrecious)
319 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
320 iEntry->second.SetPrecious(bIsPrecious);
321 UpdateCacheSize(aGuard, iEntry->second, ADD);
324 else if (bIsPrecious)
326 iEntry = mpBitmapContainer->emplace(
327 rKey,
328 CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious)
329 ).first;
330 UpdateCacheSize(aGuard, iEntry->second, ADD);
334 void BitmapCache::ReCalculateTotalCacheSize()
336 std::unique_lock aGuard (maMutex);
337 ReCalculateTotalCacheSize(aGuard);
340 void BitmapCache::ReCalculateTotalCacheSize(std::unique_lock<std::mutex>& /*rGuard*/)
342 mnNormalCacheSize = 0;
343 mnPreciousCacheSize = 0;
344 for (const auto& rEntry : *mpBitmapContainer)
346 if (rEntry.second.IsPrecious())
347 mnPreciousCacheSize += rEntry.second.GetMemorySize();
348 else
349 mnNormalCacheSize += rEntry.second.GetMemorySize();
351 mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize);
353 SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize);
356 void BitmapCache::Recycle (const BitmapCache& rCache)
358 std::unique_lock aGuard (maMutex);
360 for (const auto& rOtherEntry : *rCache.mpBitmapContainer)
362 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first));
363 if (iEntry == mpBitmapContainer->end())
365 iEntry = mpBitmapContainer->emplace(
366 rOtherEntry.first,
367 CacheEntry(mnCurrentAccessTime++, true)
368 ).first;
369 UpdateCacheSize(aGuard, iEntry->second, ADD);
371 if (iEntry != mpBitmapContainer->end())
373 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
374 iEntry->second.Recycle(rOtherEntry.second);
375 UpdateCacheSize(aGuard, iEntry->second, ADD);
380 BitmapCache::CacheIndex BitmapCache::GetCacheIndex() const
382 std::unique_lock aGuard (maMutex);
384 // Create a copy of the bitmap container.
385 SortableBitmapContainer aSortedContainer;
386 aSortedContainer.reserve(mpBitmapContainer->size());
388 // Copy the relevant entries.
389 for (const auto& rEntry : *mpBitmapContainer)
391 if ( rEntry.second.IsPrecious())
392 continue;
394 if ( ! rEntry.second.HasPreview())
395 continue;
397 aSortedContainer.emplace_back(rEntry.first, rEntry.second);
400 // Sort the remaining entries.
401 ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());
403 // Return a list with the keys of the sorted entries.
404 CacheIndex aIndex;
405 aIndex.reserve(aSortedContainer.size());
406 for (const auto& rIndexEntry : aSortedContainer)
407 aIndex.push_back(rIndexEntry.first);
408 return aIndex;
411 void BitmapCache::Compress (
412 const CacheKey& rKey,
413 const std::shared_ptr<BitmapCompressor>& rpCompressor)
415 std::unique_lock aGuard (maMutex);
417 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
418 if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview())
420 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
421 iEntry->second.Compress(rpCompressor);
422 UpdateCacheSize(aGuard, iEntry->second, ADD);
426 void BitmapCache::UpdateCacheSize (std::unique_lock<std::mutex>& /*rGuard*/, const CacheEntry& rEntry, CacheOperation eOperation)
428 sal_Int32 nEntrySize (rEntry.GetMemorySize());
429 sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize);
430 switch (eOperation)
432 case ADD:
433 rCacheSize += nEntrySize;
434 if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize)
436 mbIsFull = true;
437 SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize);
438 mpCacheCompactor->RequestCompaction();
440 break;
442 case REMOVE:
443 rCacheSize -= nEntrySize;
444 if (mnNormalCacheSize < mnMaximalNormalCacheSize)
445 mbIsFull = false;
446 break;
448 default:
449 assert(false);
450 break;
454 //===== CacheEntry ============================================================
456 BitmapCache::CacheEntry::CacheEntry(
457 sal_Int32 nLastAccessTime,
458 bool bIsPrecious)
459 : mbIsUpToDate(true),
460 mnLastAccessTime(nLastAccessTime),
461 mbIsPrecious(bIsPrecious)
465 BitmapCache::CacheEntry::CacheEntry(
466 const BitmapEx& rPreview,
467 sal_Int32 nLastAccessTime,
468 bool bIsPrecious)
469 : maPreview(rPreview),
470 mbIsUpToDate(true),
471 mnLastAccessTime(nLastAccessTime),
472 mbIsPrecious(bIsPrecious)
476 inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry)
478 if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
479 && ! (HasPreview() || HasLosslessReplacement()))
481 maPreview = rEntry.maPreview;
482 maMarkedPreview = rEntry.maMarkedPreview;
483 mpReplacement = rEntry.mpReplacement;
484 mpCompressor = rEntry.mpCompressor;
485 mnLastAccessTime = rEntry.mnLastAccessTime;
486 mbIsUpToDate = rEntry.mbIsUpToDate;
490 inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize() const
492 sal_Int32 nSize (0);
493 nSize += maPreview.GetSizeBytes();
494 nSize += maMarkedPreview.GetSizeBytes();
495 if (mpReplacement != nullptr)
496 nSize += mpReplacement->GetMemorySize();
497 return nSize;
500 void BitmapCache::CacheEntry::Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor)
502 if ( maPreview.IsEmpty())
503 return;
505 if (mpReplacement == nullptr)
507 mpReplacement = rpCompressor->Compress(maPreview);
509 #ifdef DEBUG_SD_SLSBITMAPCACHE
510 sal_uInt32 nOldSize (maPreview.GetSizeBytes());
511 sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0);
512 if (nOldSize == 0)
513 nOldSize = 1;
514 sal_Int32 nRatio (100L * nNewSize / nOldSize);
515 SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)");
516 #endif
518 mpCompressor = rpCompressor;
521 maPreview.SetEmpty();
522 maMarkedPreview.SetEmpty();
525 inline void BitmapCache::CacheEntry::Decompress()
527 if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty())
529 maPreview = mpCompressor->Decompress(*mpReplacement);
530 maMarkedPreview.SetEmpty();
531 if ( ! mpCompressor->IsLossless())
532 mbIsUpToDate = false;
536 inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx& rPreview)
538 maPreview = rPreview;
539 maMarkedPreview.SetEmpty();
540 mpReplacement.reset();
541 mpCompressor.reset();
544 bool BitmapCache::CacheEntry::HasPreview() const
546 return ! maPreview.IsEmpty();
549 inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview)
551 maMarkedPreview = rMarkedPreview;
554 inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const
556 return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless();
559 } // end of namespace ::sd::slidesorter::cache
561 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */