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 .
21 #include "SlsBitmapCache.hxx"
22 #include "SlsCacheCompactor.hxx"
23 #include "SlsBitmapCompressor.hxx"
24 #include "SlsCacheConfiguration.hxx"
27 #include <drawdoc.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 static const sal_Int32 MAXIMAL_CACHE_SIZE
= 4*1024L*1024L;
36 using namespace ::com::sun::star::uno
;
38 namespace sd
{ namespace slidesorter
{ namespace 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
82 size_t operator()(const BitmapCache::CacheKey
& p
) const
83 { return reinterpret_cast<size_t>(p
); }
86 class BitmapCache::CacheBitmapContainer
87 : public std::unordered_map
<CacheKey
, CacheEntry
, CacheHash
>
90 CacheBitmapContainer() {}
95 typedef ::std::vector
<
96 ::std::pair
< ::sd::slidesorter::cache::BitmapCache::CacheKey
,
97 ::sd::slidesorter::cache::BitmapCache::CacheEntry
>
98 > SortableBitmapContainer
;
100 /** Compare elements of the bitmap cache according to their last access
103 class AccessTimeComparator
107 const SortableBitmapContainer::value_type
& e1
,
108 const SortableBitmapContainer::value_type
& e2
)
110 return e1
.second
.GetAccessTime() < e2
.second
.GetAccessTime();
114 } // end of anonymous namespace
116 //===== BitmapCache =========================================================
118 BitmapCache::BitmapCache ()
120 mpBitmapContainer(new CacheBitmapContainer()),
121 mnNormalCacheSize(0),
122 mnPreciousCacheSize(0),
123 mnCurrentAccessTime(0),
124 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE
),
128 Any
aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize"));
129 if (aCacheSize
.has
<sal_Int32
>())
130 aCacheSize
>>= mnMaximalNormalCacheSize
;
132 mpCacheCompactor
= CacheCompactor::Create(*this,mnMaximalNormalCacheSize
);
135 BitmapCache::~BitmapCache()
140 void BitmapCache::Clear()
142 ::osl::MutexGuard
aGuard (maMutex
);
144 mpBitmapContainer
->clear();
145 mnNormalCacheSize
= 0;
146 mnPreciousCacheSize
= 0;
147 mnCurrentAccessTime
= 0;
150 bool BitmapCache::HasBitmap (const CacheKey
& rKey
)
152 ::osl::MutexGuard
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 ::osl::MutexGuard
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 ::osl::MutexGuard
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(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(iEntry
->second
, REMOVE
);
192 iEntry
->second
.Decompress();
193 UpdateCacheSize(iEntry
->second
, ADD
);
196 return iEntry
->second
.GetPreview();
199 BitmapEx
BitmapCache::GetMarkedBitmap (const CacheKey
& rKey
)
201 ::osl::MutexGuard
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 ::osl::MutexGuard
aGuard (maMutex
);
217 CacheBitmapContainer::iterator
aIterator (mpBitmapContainer
->find(rKey
));
218 if (aIterator
!= mpBitmapContainer
->end())
220 UpdateCacheSize(aIterator
->second
, REMOVE
);
221 mpBitmapContainer
->erase(aIterator
);
225 bool BitmapCache::InvalidateBitmap (const CacheKey
& rKey
)
227 ::osl::MutexGuard
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(iEntry
->second
, REMOVE
);
239 iEntry
->second
.Invalidate();
240 UpdateCacheSize(iEntry
->second
, ADD
);
248 void BitmapCache::InvalidateCache()
250 ::osl::MutexGuard
aGuard (maMutex
);
252 for (auto& rEntry
: *mpBitmapContainer
)
254 rEntry
.second
.Invalidate();
256 ReCalculateTotalCacheSize();
259 void BitmapCache::SetBitmap (
260 const CacheKey
& rKey
,
261 const BitmapEx
& rPreview
,
264 ::osl::MutexGuard
aGuard (maMutex
);
266 CacheBitmapContainer::iterator
iEntry (mpBitmapContainer
->find(rKey
));
267 if (iEntry
!= mpBitmapContainer
->end())
269 UpdateCacheSize(iEntry
->second
, REMOVE
);
270 iEntry
->second
.SetPreview(rPreview
);
271 iEntry
->second
.SetUpToDate(true);
272 iEntry
->second
.SetAccessTime(mnCurrentAccessTime
++);
276 iEntry
= mpBitmapContainer
->emplace(
278 CacheEntry(rPreview
, mnCurrentAccessTime
++, bIsPrecious
)
282 if (iEntry
!= mpBitmapContainer
->end())
283 UpdateCacheSize(iEntry
->second
, ADD
);
286 void BitmapCache::SetMarkedBitmap (
287 const CacheKey
& rKey
,
288 const BitmapEx
& rPreview
)
290 ::osl::MutexGuard
aGuard (maMutex
);
292 CacheBitmapContainer::iterator
iEntry (mpBitmapContainer
->find(rKey
));
293 if (iEntry
!= mpBitmapContainer
->end())
295 UpdateCacheSize(iEntry
->second
, REMOVE
);
296 iEntry
->second
.SetMarkedPreview(rPreview
);
297 iEntry
->second
.SetAccessTime(mnCurrentAccessTime
++);
298 UpdateCacheSize(iEntry
->second
, ADD
);
302 void BitmapCache::SetPrecious (const CacheKey
& rKey
, bool bIsPrecious
)
304 ::osl::MutexGuard
aGuard (maMutex
);
306 CacheBitmapContainer::iterator
iEntry (mpBitmapContainer
->find(rKey
));
307 if (iEntry
!= mpBitmapContainer
->end())
309 if (iEntry
->second
.IsPrecious() != bIsPrecious
)
311 UpdateCacheSize(iEntry
->second
, REMOVE
);
312 iEntry
->second
.SetPrecious(bIsPrecious
);
313 UpdateCacheSize(iEntry
->second
, ADD
);
316 else if (bIsPrecious
)
318 iEntry
= mpBitmapContainer
->emplace(
320 CacheEntry(BitmapEx(), mnCurrentAccessTime
++, bIsPrecious
)
322 UpdateCacheSize(iEntry
->second
, ADD
);
326 void BitmapCache::ReCalculateTotalCacheSize()
328 ::osl::MutexGuard
aGuard (maMutex
);
330 mnNormalCacheSize
= 0;
331 mnPreciousCacheSize
= 0;
332 for (const auto& rEntry
: *mpBitmapContainer
)
334 if (rEntry
.second
.IsPrecious())
335 mnPreciousCacheSize
+= rEntry
.second
.GetMemorySize();
337 mnNormalCacheSize
+= rEntry
.second
.GetMemorySize();
339 mbIsFull
= (mnNormalCacheSize
>= mnMaximalNormalCacheSize
);
341 SAL_INFO("sd.sls", OSL_THIS_FUNC
<< ": cache size is " << mnNormalCacheSize
<< "/" << mnPreciousCacheSize
);
344 void BitmapCache::Recycle (const BitmapCache
& rCache
)
346 ::osl::MutexGuard
aGuard (maMutex
);
348 for (const auto& rOtherEntry
: *rCache
.mpBitmapContainer
)
350 CacheBitmapContainer::iterator
iEntry (mpBitmapContainer
->find(rOtherEntry
.first
));
351 if (iEntry
== mpBitmapContainer
->end())
353 iEntry
= mpBitmapContainer
->emplace(
355 CacheEntry(mnCurrentAccessTime
++, true)
357 UpdateCacheSize(iEntry
->second
, ADD
);
359 if (iEntry
!= mpBitmapContainer
->end())
361 UpdateCacheSize(iEntry
->second
, REMOVE
);
362 iEntry
->second
.Recycle(rOtherEntry
.second
);
363 UpdateCacheSize(iEntry
->second
, ADD
);
368 ::std::unique_ptr
<BitmapCache::CacheIndex
> BitmapCache::GetCacheIndex() const
370 ::osl::MutexGuard
aGuard (maMutex
);
372 // Create a copy of the bitmap container.
373 SortableBitmapContainer aSortedContainer
;
374 aSortedContainer
.reserve(mpBitmapContainer
->size());
376 // Copy the relevant entries.
377 for (const auto& rEntry
: *mpBitmapContainer
)
379 if ( rEntry
.second
.IsPrecious())
382 if ( ! rEntry
.second
.HasPreview())
385 aSortedContainer
.emplace_back(rEntry
.first
, rEntry
.second
);
388 // Sort the remaining entries.
389 ::std::sort(aSortedContainer
.begin(), aSortedContainer
.end(), AccessTimeComparator());
391 // Return a list with the keys of the sorted entries.
392 ::std::unique_ptr
<CacheIndex
> pIndex(new CacheIndex
);
393 pIndex
->reserve(aSortedContainer
.size());
394 for (const auto& rIndexEntry
: aSortedContainer
)
395 pIndex
->push_back(rIndexEntry
.first
);
399 void BitmapCache::Compress (
400 const CacheKey
& rKey
,
401 const std::shared_ptr
<BitmapCompressor
>& rpCompressor
)
403 ::osl::MutexGuard
aGuard (maMutex
);
405 CacheBitmapContainer::iterator
iEntry (mpBitmapContainer
->find(rKey
));
406 if (iEntry
!= mpBitmapContainer
->end() && iEntry
->second
.HasPreview())
408 UpdateCacheSize(iEntry
->second
, REMOVE
);
409 iEntry
->second
.Compress(rpCompressor
);
410 UpdateCacheSize(iEntry
->second
, ADD
);
414 void BitmapCache::UpdateCacheSize (const CacheEntry
& rEntry
, CacheOperation eOperation
)
416 sal_Int32
nEntrySize (rEntry
.GetMemorySize());
417 sal_Int32
& rCacheSize (rEntry
.IsPrecious() ? mnPreciousCacheSize
: mnNormalCacheSize
);
421 rCacheSize
+= nEntrySize
;
422 if ( ! rEntry
.IsPrecious() && mnNormalCacheSize
>mnMaximalNormalCacheSize
)
425 SAL_INFO("sd.sls", OSL_THIS_FUNC
<< ": cache size is " << mnNormalCacheSize
<< " > " << mnMaximalNormalCacheSize
);
426 mpCacheCompactor
->RequestCompaction();
431 rCacheSize
-= nEntrySize
;
432 if (mnNormalCacheSize
< mnMaximalNormalCacheSize
)
442 //===== CacheEntry ============================================================
444 BitmapCache::CacheEntry::CacheEntry(
445 sal_Int32 nLastAccessTime
,
450 mnLastAccessTime(nLastAccessTime
),
451 mbIsPrecious(bIsPrecious
)
455 BitmapCache::CacheEntry::CacheEntry(
456 const BitmapEx
& rPreview
,
457 sal_Int32 nLastAccessTime
,
459 : maPreview(rPreview
),
462 mnLastAccessTime(nLastAccessTime
),
463 mbIsPrecious(bIsPrecious
)
467 inline void BitmapCache::CacheEntry::Recycle (const CacheEntry
& rEntry
)
469 if ((rEntry
.HasPreview() || rEntry
.HasLosslessReplacement())
470 && ! (HasPreview() || HasLosslessReplacement()))
472 maPreview
= rEntry
.maPreview
;
473 maMarkedPreview
= rEntry
.maMarkedPreview
;
474 mpReplacement
= rEntry
.mpReplacement
;
475 mpCompressor
= rEntry
.mpCompressor
;
476 mnLastAccessTime
= rEntry
.mnLastAccessTime
;
477 mbIsUpToDate
= rEntry
.mbIsUpToDate
;
481 inline sal_Int32
BitmapCache::CacheEntry::GetMemorySize() const
484 nSize
+= maPreview
.GetSizeBytes();
485 nSize
+= maMarkedPreview
.GetSizeBytes();
486 if (mpReplacement
!= nullptr)
487 nSize
+= mpReplacement
->GetMemorySize();
491 void BitmapCache::CacheEntry::Compress (const std::shared_ptr
<BitmapCompressor
>& rpCompressor
)
493 if ( maPreview
.IsEmpty())
496 if (mpReplacement
== nullptr)
498 mpReplacement
= rpCompressor
->Compress(maPreview
);
500 #ifdef DEBUG_SD_SLSBITMAPCACHE
501 sal_uInt32
nOldSize (maPreview
.GetSizeBytes());
502 sal_uInt32
nNewSize (mpReplacement
.get()!=NULL
? mpReplacement
->GetMemorySize() : 0);
505 sal_Int32
nRatio (100L * nNewSize
/ nOldSize
);
506 SAL_INFO("sd.sls", OSL_THIS_FUNC
<< ": compressing bitmap for " << %x
<< " from " << nOldSize
<< " to " << nNewSize
<< " bytes (" << nRatio
<< "%)");
509 mpCompressor
= rpCompressor
;
512 maPreview
.SetEmpty();
513 maMarkedPreview
.SetEmpty();
516 inline void BitmapCache::CacheEntry::Decompress()
518 if (mpReplacement
!= nullptr && mpCompressor
!= nullptr && maPreview
.IsEmpty())
520 maPreview
= mpCompressor
->Decompress(*mpReplacement
);
521 maMarkedPreview
.SetEmpty();
522 if ( ! mpCompressor
->IsLossless())
523 mbIsUpToDate
= false;
527 inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx
& rPreview
)
529 maPreview
= rPreview
;
530 maMarkedPreview
.SetEmpty();
531 mpReplacement
.reset();
532 mpCompressor
.reset();
535 bool BitmapCache::CacheEntry::HasPreview() const
537 return ! maPreview
.IsEmpty();
540 inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx
& rMarkedPreview
)
542 maMarkedPreview
= rMarkedPreview
;
545 inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const
547 return mpReplacement
!= nullptr && mpCompressor
!= nullptr && mpCompressor
->IsLossless();
550 } } } // end of namespace ::sd::slidesorter::cache
552 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */