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 <officecfg/Office/Impress.hxx>
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
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
; }
70 BitmapEx maMarkedPreview
;
71 std::shared_ptr
<BitmapReplacement
> mpReplacement
;
72 std::shared_ptr
<BitmapCompressor
> mpCompressor
;
74 sal_Int32 mnLastAccessTime
;
75 // When this flag is set then the bitmap is not modified by a cache
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
>
94 CacheBitmapContainer() {}
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
107 class AccessTimeComparator
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
),
130 mnMaximalNormalCacheSize
= officecfg::Office::Impress::MultiPaneGUI::SlideSorterBar::PreviewCache::CacheSize::get();
132 mpCacheCompactor
= CacheCompactor::Create(*this,mnMaximalNormalCacheSize
);
135 BitmapCache::~BitmapCache()
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();
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);
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();
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
);
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
,
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
,
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
++);
284 iEntry
= mpBitmapContainer
->emplace(
286 CacheEntry(rPreview
, mnCurrentAccessTime
++, bIsPrecious
)
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(
328 CacheEntry(BitmapEx(), mnCurrentAccessTime
++, bIsPrecious
)
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();
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(
367 CacheEntry(mnCurrentAccessTime
++, true)
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())
394 if ( ! rEntry
.second
.HasPreview())
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.
405 aIndex
.reserve(aSortedContainer
.size());
406 for (const auto& rIndexEntry
: aSortedContainer
)
407 aIndex
.push_back(rIndexEntry
.first
);
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
);
433 rCacheSize
+= nEntrySize
;
434 if ( ! rEntry
.IsPrecious() && mnNormalCacheSize
>mnMaximalNormalCacheSize
)
437 SAL_INFO("sd.sls", __func__
<< ": cache size is " << mnNormalCacheSize
<< " > " << mnMaximalNormalCacheSize
);
438 mpCacheCompactor
->RequestCompaction();
443 rCacheSize
-= nEntrySize
;
444 if (mnNormalCacheSize
< mnMaximalNormalCacheSize
)
454 //===== CacheEntry ============================================================
456 BitmapCache::CacheEntry::CacheEntry(
457 sal_Int32 nLastAccessTime
,
459 : mbIsUpToDate(true),
460 mnLastAccessTime(nLastAccessTime
),
461 mbIsPrecious(bIsPrecious
)
465 BitmapCache::CacheEntry::CacheEntry(
466 const BitmapEx
& rPreview
,
467 sal_Int32 nLastAccessTime
,
469 : maPreview(rPreview
),
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
493 nSize
+= maPreview
.GetSizeBytes();
494 nSize
+= maMarkedPreview
.GetSizeBytes();
495 if (mpReplacement
!= nullptr)
496 nSize
+= mpReplacement
->GetMemorySize();
500 void BitmapCache::CacheEntry::Compress (const std::shared_ptr
<BitmapCompressor
>& rpCompressor
)
502 if ( maPreview
.IsEmpty())
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);
514 sal_Int32
nRatio (100L * nNewSize
/ nOldSize
);
515 SAL_INFO("sd.sls", __func__
<< ": compressing bitmap for " << %x
<< " from " << nOldSize
<< " to " << nNewSize
<< " bytes (" << nRatio
<< "%)");
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: */