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 "SlsBitmapCache.hxx"
21 #include "SlsCacheCompactor.hxx"
22 #include "SlsBitmapCompressor.hxx"
23 #include "SlsCacheConfiguration.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
41 CacheEntry(const Bitmap
& rBitmap
, sal_Int32 nLastAccessTime
, bool bIsPrecious
);
42 CacheEntry(sal_Int32 nLastAccessTime
, bool bIsPrecious
);
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
; }
69 Bitmap maMarkedPreview
;
70 ::boost::shared_ptr
<BitmapReplacement
> mpReplacement
;
71 ::boost::shared_ptr
<BitmapCompressor
> mpCompressor
;
74 sal_Int32 mnLastAccessTime
;
75 // When this flag is set then the bitmap is not modified by a cache
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
>
91 CacheBitmapContainer() {}
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
104 class AccessTimeComparator
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
)
121 mpBitmapContainer(new CacheBitmapContainer()),
122 mnNormalCacheSize(0),
123 mnPreciousCacheSize(0),
124 mnCurrentAccessTime(0),
125 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE
),
129 if (nMaximalNormalCacheSize
> 0)
130 mnMaximalNormalCacheSize
= nMaximalNormalCacheSize
;
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()
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();
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);
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();
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
);
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
,
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
++);
283 iEntry
= mpBitmapContainer
->insert(CacheBitmapContainer::value_type (
285 CacheEntry(rPreview
, mnCurrentAccessTime
++, bIsPrecious
))
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 (
327 CacheEntry(Bitmap(), mnCurrentAccessTime
++, bIsPrecious
))
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();
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();
361 CacheBitmapContainer::iterator
iEntry (mpBitmapContainer
->find(iOtherEntry
->first
));
362 if (iEntry
== mpBitmapContainer
->end())
364 iEntry
= mpBitmapContainer
->insert(CacheBitmapContainer::value_type (
366 CacheEntry(mnCurrentAccessTime
++, true))
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())
396 if ( ! bIncludeNoPreview
&& ! iEntry
->second
.HasPreview())
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
);
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
);
437 rCacheSize
+= nEntrySize
;
438 if ( ! rEntry
.IsPrecious() && mnNormalCacheSize
>mnMaximalNormalCacheSize
)
441 SAL_INFO("sd.sls", OSL_THIS_FUNC
<< ": cache size is " << mnNormalCacheSize
<< " > " << mnMaximalNormalCacheSize
);
442 mpCacheCompactor
->RequestCompaction();
447 rCacheSize
-= nEntrySize
;
448 if (mnNormalCacheSize
< mnMaximalNormalCacheSize
)
458 //===== CacheEntry ============================================================
460 BitmapCache::CacheEntry::CacheEntry(
461 sal_Int32 nLastAccessTime
,
466 mnLastAccessTime(nLastAccessTime
),
467 mbIsPrecious(bIsPrecious
)
471 BitmapCache::CacheEntry::CacheEntry(
472 const Bitmap
& rPreview
,
473 sal_Int32 nLastAccessTime
,
475 : maPreview(rPreview
),
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
500 nSize
+= maPreview
.GetSizeBytes();
501 nSize
+= maMarkedPreview
.GetSizeBytes();
502 if (mpReplacement
.get() != NULL
)
503 nSize
+= mpReplacement
->GetMemorySize();
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);
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
<< "%)");
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: */