LibreOffice Module sd (master) 1
SlsBitmapCache.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <memory>
21#include <unordered_map>
22#include "SlsBitmapCache.hxx"
23#include "SlsCacheCompactor.hxx"
26
27#include <sal/log.hxx>
28
29// Define the default value for the maximal cache size that is used for
30// previews that are currently not visible. The visible previews are all
31// held in memory at all times. This default is used only when the
32// configuration does not have a value.
33const sal_Int32 MAXIMAL_CACHE_SIZE = 4*1024L*1024L;
34
35using namespace ::com::sun::star::uno;
36
37namespace sd::slidesorter::cache {
38
40{
41public:
42 CacheEntry(const BitmapEx& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious);
43 CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious);
44 inline void Recycle (const CacheEntry& rEntry);
45 inline sal_Int32 GetMemorySize() const;
46 void Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor);
47 inline void Decompress();
48
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; }
53
54 const BitmapEx& GetPreview() const { return maPreview; }
55 inline void SetPreview (const BitmapEx& rPreview);
56 bool HasPreview() const;
57
58 const BitmapEx& GetMarkedPreview() const { return maMarkedPreview; }
59 inline void SetMarkedPreview (const BitmapEx& rMarkePreview);
60
61 bool HasReplacement() const { return (mpReplacement != nullptr); }
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; }
66
67private:
70 std::shared_ptr<BitmapReplacement> mpReplacement;
71 std::shared_ptr<BitmapCompressor> mpCompressor;
74 // When this flag is set then the bitmap is not modified by a cache
75 // compactor.
77};
78
79namespace {
80
81class CacheHash {
82public:
83 size_t operator()(const BitmapCache::CacheKey& p) const
84 { return reinterpret_cast<size_t>(p); }
85};
86
87}
88
90 : public std::unordered_map<CacheKey, CacheEntry, CacheHash>
91{
92public:
94};
95
96namespace {
97
98typedef ::std::vector<
101 > SortableBitmapContainer;
102
106 class AccessTimeComparator
107 {
108 public:
109 bool operator () (
110 const SortableBitmapContainer::value_type& e1,
111 const SortableBitmapContainer::value_type& e2)
112 {
113 return e1.second.GetAccessTime() < e2.second.GetAccessTime();
114 }
115 };
116
117} // end of anonymous namespace
118
119//===== BitmapCache =========================================================
120
122 : mpBitmapContainer(new CacheBitmapContainer()),
123 mnNormalCacheSize(0),
124 mnPreciousCacheSize(0),
125 mnCurrentAccessTime(0),
126 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
127 mbIsFull(false)
128{
129 Any aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize"));
130 if (aCacheSize.has<sal_Int32>())
131 aCacheSize >>= mnMaximalNormalCacheSize;
132
134}
135
137{
138 Clear();
139}
140
142{
143 std::unique_lock aGuard (maMutex);
144
145 mpBitmapContainer->clear();
149}
150
152{
153 std::unique_lock aGuard (maMutex);
154
155 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
156 return (iEntry != mpBitmapContainer->end()
157 && (iEntry->second.HasPreview() || iEntry->second.HasReplacement()));
158}
159
161{
162 std::unique_lock aGuard (maMutex);
163
164 bool bIsUpToDate = false;
165 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
166 if (aIterator != mpBitmapContainer->end())
167 bIsUpToDate = aIterator->second.IsUpToDate();
168
169 return bIsUpToDate;
170}
171
173{
174 std::unique_lock aGuard (maMutex);
175
176 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
177 if (iEntry == mpBitmapContainer->end())
178 {
179 // Create an empty bitmap for the given key that acts as placeholder
180 // until we are given the real one. Mark it as not being up to date.
181 SetBitmap(aGuard, rKey, BitmapEx(), false);
182 iEntry = mpBitmapContainer->find(rKey);
183 iEntry->second.SetUpToDate(false);
184 }
185 else
186 {
187 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
188
189 // Maybe we have to decompress the preview.
190 if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement())
191 {
192 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
193 iEntry->second.Decompress();
194 UpdateCacheSize(aGuard, iEntry->second, ADD);
195 }
196 }
197 return iEntry->second.GetPreview();
198}
199
201{
202 std::unique_lock aGuard (maMutex);
203
204 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
205 if (iEntry != mpBitmapContainer->end())
206 {
207 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
208 return iEntry->second.GetMarkedPreview();
209 }
210 else
211 return BitmapEx();
212}
213
215{
216 std::unique_lock aGuard (maMutex);
217
218 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
219 if (aIterator != mpBitmapContainer->end())
220 {
221 UpdateCacheSize(aGuard, aIterator->second, REMOVE);
222 mpBitmapContainer->erase(aIterator);
223 }
224}
225
227{
228 std::unique_lock aGuard (maMutex);
229
230 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
231 if (iEntry != mpBitmapContainer->end())
232 {
233 iEntry->second.SetUpToDate(false);
234
235 // When there is a preview then we release the replacement. The
236 // preview itself is kept until a new one is created.
237 if (iEntry->second.HasPreview())
238 {
239 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
240 iEntry->second.Invalidate();
241 UpdateCacheSize(aGuard, iEntry->second, ADD);
242 }
243 return true;
244 }
245 else
246 return false;
247}
248
250{
251 std::unique_lock aGuard (maMutex);
252
253 for (auto& rEntry : *mpBitmapContainer)
254 {
255 rEntry.second.Invalidate();
256 }
258}
259
261 const CacheKey& rKey,
262 const BitmapEx& rPreview,
263 bool bIsPrecious)
264{
265 std::unique_lock aGuard (maMutex);
266 SetBitmap(aGuard, rKey, rPreview, bIsPrecious);
267}
268
270 std::unique_lock<std::mutex>& rGuard,
271 const CacheKey& rKey,
272 const BitmapEx& rPreview,
273 bool bIsPrecious)
274{
275 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
276 if (iEntry != mpBitmapContainer->end())
277 {
278 UpdateCacheSize(rGuard, iEntry->second, REMOVE);
279 iEntry->second.SetPreview(rPreview);
280 iEntry->second.SetUpToDate(true);
281 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
282 }
283 else
284 {
285 iEntry = mpBitmapContainer->emplace(
286 rKey,
287 CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious)
288 ).first;
289 }
290
291 if (iEntry != mpBitmapContainer->end())
292 UpdateCacheSize(rGuard, iEntry->second, ADD);
293}
294
296 const CacheKey& rKey,
297 const BitmapEx& rPreview)
298{
299 std::unique_lock aGuard (maMutex);
300
301 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
302 if (iEntry != mpBitmapContainer->end())
303 {
304 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
305 iEntry->second.SetMarkedPreview(rPreview);
306 iEntry->second.SetAccessTime(mnCurrentAccessTime++);
307 UpdateCacheSize(aGuard, iEntry->second, ADD);
308 }
309}
310
311void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious)
312{
313 std::unique_lock aGuard (maMutex);
314
315 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
316 if (iEntry != mpBitmapContainer->end())
317 {
318 if (iEntry->second.IsPrecious() != bIsPrecious)
319 {
320 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
321 iEntry->second.SetPrecious(bIsPrecious);
322 UpdateCacheSize(aGuard, iEntry->second, ADD);
323 }
324 }
325 else if (bIsPrecious)
326 {
327 iEntry = mpBitmapContainer->emplace(
328 rKey,
329 CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious)
330 ).first;
331 UpdateCacheSize(aGuard, iEntry->second, ADD);
332 }
333}
334
336{
337 std::unique_lock aGuard (maMutex);
339}
340
341void BitmapCache::ReCalculateTotalCacheSize(std::unique_lock<std::mutex>& /*rGuard*/)
342{
345 for (const auto& rEntry : *mpBitmapContainer)
346 {
347 if (rEntry.second.IsPrecious())
348 mnPreciousCacheSize += rEntry.second.GetMemorySize();
349 else
350 mnNormalCacheSize += rEntry.second.GetMemorySize();
351 }
353
354 SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize);
355}
356
358{
359 std::unique_lock aGuard (maMutex);
360
361 for (const auto& rOtherEntry : *rCache.mpBitmapContainer)
362 {
363 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first));
364 if (iEntry == mpBitmapContainer->end())
365 {
366 iEntry = mpBitmapContainer->emplace(
367 rOtherEntry.first,
369 ).first;
370 UpdateCacheSize(aGuard, iEntry->second, ADD);
371 }
372 if (iEntry != mpBitmapContainer->end())
373 {
374 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
375 iEntry->second.Recycle(rOtherEntry.second);
376 UpdateCacheSize(aGuard, iEntry->second, ADD);
377 }
378 }
379}
380
382{
383 std::unique_lock aGuard (maMutex);
384
385 // Create a copy of the bitmap container.
386 SortableBitmapContainer aSortedContainer;
387 aSortedContainer.reserve(mpBitmapContainer->size());
388
389 // Copy the relevant entries.
390 for (const auto& rEntry : *mpBitmapContainer)
391 {
392 if ( rEntry.second.IsPrecious())
393 continue;
394
395 if ( ! rEntry.second.HasPreview())
396 continue;
397
398 aSortedContainer.emplace_back(rEntry.first, rEntry.second);
399 }
400
401 // Sort the remaining entries.
402 ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());
403
404 // Return a list with the keys of the sorted entries.
406 aIndex.reserve(aSortedContainer.size());
407 for (const auto& rIndexEntry : aSortedContainer)
408 aIndex.push_back(rIndexEntry.first);
409 return aIndex;
410}
411
413 const CacheKey& rKey,
414 const std::shared_ptr<BitmapCompressor>& rpCompressor)
415{
416 std::unique_lock aGuard (maMutex);
417
418 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
419 if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview())
420 {
421 UpdateCacheSize(aGuard, iEntry->second, REMOVE);
422 iEntry->second.Compress(rpCompressor);
423 UpdateCacheSize(aGuard, iEntry->second, ADD);
424 }
425}
426
427void BitmapCache::UpdateCacheSize (std::unique_lock<std::mutex>& /*rGuard*/, const CacheEntry& rEntry, CacheOperation eOperation)
428{
429 sal_Int32 nEntrySize (rEntry.GetMemorySize());
430 sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize);
431 switch (eOperation)
432 {
433 case ADD:
434 rCacheSize += nEntrySize;
436 {
437 mbIsFull = true;
438 SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize);
439 mpCacheCompactor->RequestCompaction();
440 }
441 break;
442
443 case REMOVE:
444 rCacheSize -= nEntrySize;
446 mbIsFull = false;
447 break;
448
449 default:
450 assert(false);
451 break;
452 }
453}
454
455//===== CacheEntry ============================================================
456
458 sal_Int32 nLastAccessTime,
459 bool bIsPrecious)
460 : mbIsUpToDate(true),
461 mnLastAccessTime(nLastAccessTime),
462 mbIsPrecious(bIsPrecious)
463{
464}
465
467 const BitmapEx& rPreview,
468 sal_Int32 nLastAccessTime,
469 bool bIsPrecious)
470 : maPreview(rPreview),
471 mbIsUpToDate(true),
472 mnLastAccessTime(nLastAccessTime),
473 mbIsPrecious(bIsPrecious)
474{
475}
476
478{
479 if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
480 && ! (HasPreview() || HasLosslessReplacement()))
481 {
482 maPreview = rEntry.maPreview;
483 maMarkedPreview = rEntry.maMarkedPreview;
484 mpReplacement = rEntry.mpReplacement;
485 mpCompressor = rEntry.mpCompressor;
486 mnLastAccessTime = rEntry.mnLastAccessTime;
487 mbIsUpToDate = rEntry.mbIsUpToDate;
488 }
489}
490
492{
493 sal_Int32 nSize (0);
494 nSize += maPreview.GetSizeBytes();
495 nSize += maMarkedPreview.GetSizeBytes();
496 if (mpReplacement != nullptr)
497 nSize += mpReplacement->GetMemorySize();
498 return nSize;
499}
500
501void BitmapCache::CacheEntry::Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor)
502{
503 if ( maPreview.IsEmpty())
504 return;
505
506 if (mpReplacement == nullptr)
507 {
508 mpReplacement = rpCompressor->Compress(maPreview);
509
510#ifdef DEBUG_SD_SLSBITMAPCACHE
511 sal_uInt32 nOldSize (maPreview.GetSizeBytes());
512 sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0);
513 if (nOldSize == 0)
514 nOldSize = 1;
515 sal_Int32 nRatio (100L * nNewSize / nOldSize);
516 SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)");
517#endif
518
519 mpCompressor = rpCompressor;
520 }
521
522 maPreview.SetEmpty();
523 maMarkedPreview.SetEmpty();
524}
525
527{
528 if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty())
529 {
530 maPreview = mpCompressor->Decompress(*mpReplacement);
531 maMarkedPreview.SetEmpty();
532 if ( ! mpCompressor->IsLossless())
533 mbIsUpToDate = false;
534 }
535}
536
538{
539 maPreview = rPreview;
540 maMarkedPreview.SetEmpty();
541 mpReplacement.reset();
542 mpCompressor.reset();
543}
544
546{
547 return ! maPreview.IsEmpty();
548}
549
550inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview)
551{
552 maMarkedPreview = rMarkedPreview;
553}
554
556{
557 return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless();
558}
559
560} // end of namespace ::sd::slidesorter::cache
561
562/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const sal_Int32 MAXIMAL_CACHE_SIZE
void SetEmpty()
std::shared_ptr< BitmapCompressor > mpCompressor
void Compress(const std::shared_ptr< BitmapCompressor > &rpCompressor)
CacheEntry(const BitmapEx &rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious)
void SetMarkedPreview(const BitmapEx &rMarkePreview)
std::shared_ptr< BitmapReplacement > mpReplacement
This low level cache is the actual bitmap container.
void ReCalculateTotalCacheSize()
Calculate the cache size.
void Recycle(const BitmapCache &rCache)
Use the previews in the given cache to initialize missing previews.
bool InvalidateBitmap(const CacheKey &rKey)
Mark the specified preview bitmap as not being up-to-date anymore.
sal_Int32 mnMaximalNormalCacheSize
The maximal cache size for the off-screen preview bitmaps.
void SetPrecious(const CacheKey &rKey, bool bIsPrecious)
Mark the specified preview bitmap as precious, i.e.
::std::vector< CacheKey > CacheIndex
std::unique_ptr< CacheBitmapContainer > mpBitmapContainer
::std::unique_ptr< CacheCompactor > mpCacheCompactor
The cache compactor is used to reduce the number of bytes used by off-screen preview bitmaps.
BitmapEx GetBitmap(const CacheKey &rKey)
Return the preview bitmap for the given contact object.
void SetMarkedBitmap(const CacheKey &rKey, const BitmapEx &rPreview)
Add or replace a marked bitmap for the given key.
void Compress(const CacheKey &rKey, const std::shared_ptr< BitmapCompressor > &rpCompressor)
Compress the specified preview bitmap with the given bitmap compressor.
bool HasBitmap(const CacheKey &rKey)
Return <TRUE> when a preview bitmap exists for the given key.
void SetBitmap(const CacheKey &rKey, const BitmapEx &rPreview, bool bIsPrecious)
Add or replace a bitmap for the given key.
sal_Int32 mnNormalCacheSize
Total size of bytes that are occupied by bitmaps in the cache for whom the slides are currently not i...
void Clear()
Remove all preview bitmaps from the cache.
CacheIndex GetCacheIndex() const
Return a list of sorted cache keys that represent an index into (a part of) the cache.
void ReleaseBitmap(const CacheKey &rKey)
Release the reference to the preview bitmap that is associated with the given key.
bool BitmapIsUpToDate(const CacheKey &rKey)
Return <TRUE> when a preview bitmap exists for the given key and when it is up-to-date.
const SdrPage * CacheKey
The key for looking up preview bitmaps is a pointer to an SdrPage object.
~BitmapCache()
The destructor clears the cache and releases all bitmaps still in it.
CacheOperation
Update mnNormalCacheSize or mnPreciousCacheSize according to the precious flag of the specified previ...
BitmapCache()
Create a new cache for bitmap objects.
bool mbIsFull
This flag stores if the cache is or recently was full, i.e.
void UpdateCacheSize(std::unique_lock< std::mutex > &rGuard, const CacheEntry &rKey, CacheOperation eOperation)
BitmapEx GetMarkedBitmap(const CacheKey &rKey)
Return the marked preview bitmap for the given contact object.
void InvalidateCache()
Mark all preview bitmaps as not being up-to-date anymore.
sal_Int32 mnCurrentAccessTime
At the moment the access time is not an actual time or date value but a counter that is increased wit...
sal_Int32 mnPreciousCacheSize
Total size of bytes that are occupied by bitmaps in the cache for whom the slides are currently visib...
static ::std::unique_ptr< CacheCompactor > Create(BitmapCache &rCache, sal_Int32 nMaximalCacheSize)
Create a new instance of the CacheCompactor interface class.
static std::shared_ptr< CacheConfiguration > Instance()
Return an instance to this class.
float x
std::deque< AttacherIndex_Impl > aIndex
void * p
#define SAL_INFO(area, stream)
return NULL
const char GetValue[]