xref: /AOO41X/main/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sd.hxx"
30 
31 #include "SlsBitmapCache.hxx"
32 #include "SlsCacheCompactor.hxx"
33 #include "SlsBitmapCompressor.hxx"
34 #include "SlsCacheConfiguration.hxx"
35 
36 #include "sdpage.hxx"
37 #include "drawdoc.hxx"
38 
39 // Uncomment the following define for some more OSL_TRACE messages.
40 #ifdef DEBUG
41 //#define VERBOSE
42 #endif
43 
44 // Define the default value for the maximal cache size that is used for
45 // previews that are currently not visible.  The visible previews are all
46 // held in memory at all times.  This default is used only when the
47 // configuration does not have a value.
48 static const sal_Int32 MAXIMAL_CACHE_SIZE = 4L*1024L*1024L;
49 
50 using namespace ::com::sun::star::uno;
51 
52 namespace sd { namespace slidesorter { namespace cache {
53 
54 class BitmapCache::CacheEntry
55 {
56 public:
57     CacheEntry(const Bitmap& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious);
58     CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious);
59     ~CacheEntry (void) {};
60     inline void Recycle (const CacheEntry& rEntry);
61     inline sal_Int32 GetMemorySize (void) const;
62     void Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor);
63     inline void Decompress (void);
64 
65     bool IsUpToDate (void) const { return mbIsUpToDate; }
66     void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; }
67     sal_Int32 GetAccessTime (void) const { return mnLastAccessTime; }
68     void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; }
69 
70     Bitmap GetPreview (void) const { return maPreview; }
71     inline void SetPreview (const Bitmap& rPreview);
72     bool HasPreview (void) const;
73 
74     Bitmap GetMarkedPreview (void) const { return maMarkedPreview; }
75     inline void SetMarkedPreview (const Bitmap& rMarkePreview);
76     bool HasMarkedPreview (void) const;
77 
78     bool HasReplacement (void) const { return (mpReplacement.get() != NULL); }
79     inline bool HasLosslessReplacement (void) const;
80     void Clear (void) { maPreview.SetEmpty(); maMarkedPreview.SetEmpty();
81         mpReplacement.reset(); mpCompressor.reset(); }
82     void Invalidate (void) { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; }
83     bool IsPrecious (void) const { return mbIsPrecious; }
84     void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; }
85 
86 private:
87     Bitmap maPreview;
88     Bitmap maMarkedPreview;
89     ::boost::shared_ptr<BitmapReplacement> mpReplacement;
90     ::boost::shared_ptr<BitmapCompressor> mpCompressor;
91     Size maBitmapSize;
92     bool mbIsUpToDate;
93     sal_Int32 mnLastAccessTime;
94     // When this flag is set then the bitmap is not modified by a cache
95     // compactor.
96     bool mbIsPrecious;
97 };
98 class CacheEntry;
99 
100 class CacheHash {
101 public:
102     size_t operator()(const BitmapCache::CacheKey& p) const
103     { return (size_t)p; }
104 };
105 
106 class BitmapCache::CacheBitmapContainer
107     : public ::std::hash_map<CacheKey, CacheEntry, CacheHash>
108 {
109 public:
110     CacheBitmapContainer (void) {}
111 };
112 
113 namespace {
114 
115 typedef ::std::vector<
116     ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey,
117       ::sd::slidesorter::cache::BitmapCache::CacheEntry>
118     > SortableBitmapContainer;
119 
120     /** Compare elements of the bitmap cache according to their last access
121         time.
122     */
123     class AccessTimeComparator
124     {
125     public:
126         bool operator () (
127             const SortableBitmapContainer::value_type& e1,
128             const SortableBitmapContainer::value_type& e2)
129         {
130             return e1.second.GetAccessTime() < e2.second.GetAccessTime();
131         }
132     };
133 
134 
135 } // end of anonymous namespace
136 
137 
138 //=====  BitmapCache  =========================================================
139 
140 BitmapCache::BitmapCache (const sal_Int32 nMaximalNormalCacheSize)
141     : maMutex(),
142       mpBitmapContainer(new CacheBitmapContainer()),
143       mnNormalCacheSize(0),
144       mnPreciousCacheSize(0),
145       mnCurrentAccessTime(0),
146       mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
147       mpCacheCompactor(),
148       mbIsFull(false)
149 {
150     if (nMaximalNormalCacheSize > 0)
151         mnMaximalNormalCacheSize = nMaximalNormalCacheSize;
152     else
153     {
154         Any aCacheSize (CacheConfiguration::Instance()->GetValue(
155         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CacheSize"))));
156         if (aCacheSize.has<sal_Int32>())
157             aCacheSize >>= mnMaximalNormalCacheSize;
158     }
159 
160     mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize);
161 }
162 
163 
164 
165 
166 BitmapCache::~BitmapCache (void)
167 {
168     Clear();
169 }
170 
171 
172 
173 
174 void BitmapCache::Clear (void)
175 {
176     ::osl::MutexGuard aGuard (maMutex);
177 
178     mpBitmapContainer->clear();
179     mnNormalCacheSize = 0;
180     mnPreciousCacheSize = 0;
181     mnCurrentAccessTime = 0;
182 }
183 
184 
185 
186 
187 bool BitmapCache::IsFull (void) const
188 {
189     return mbIsFull;
190 }
191 
192 
193 
194 
195 sal_Int32 BitmapCache::GetSize (void)
196 {
197     return mnNormalCacheSize;
198 }
199 
200 
201 
202 
203 bool BitmapCache::HasBitmap (const CacheKey& rKey)
204 {
205     ::osl::MutexGuard aGuard (maMutex);
206 
207     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
208     return (iEntry != mpBitmapContainer->end()
209         && (iEntry->second.HasPreview() || iEntry->second.HasReplacement()));
210 }
211 
212 
213 
214 
215 bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey)
216 {
217     ::osl::MutexGuard aGuard (maMutex);
218 
219     bool bIsUpToDate = false;
220     CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
221     if (aIterator != mpBitmapContainer->end())
222         bIsUpToDate = aIterator->second.IsUpToDate();
223 
224     return bIsUpToDate;
225 }
226 
227 
228 
229 
230 Bitmap BitmapCache::GetBitmap (const CacheKey& rKey)
231 {
232     ::osl::MutexGuard aGuard (maMutex);
233 
234     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
235     if (iEntry == mpBitmapContainer->end())
236     {
237         // Create an empty bitmap for the given key that acts as placeholder
238         // until we are given the real one.  Mark it as not being up to date.
239         SetBitmap(rKey, Bitmap(), false);
240         iEntry = mpBitmapContainer->find(rKey);
241         iEntry->second.SetUpToDate(false);
242     }
243     else
244     {
245         iEntry->second.SetAccessTime(mnCurrentAccessTime++);
246 
247         // Maybe we have to decompress the preview.
248         if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement())
249         {
250             UpdateCacheSize(iEntry->second, REMOVE);
251             iEntry->second.Decompress();
252             UpdateCacheSize(iEntry->second, ADD);
253         }
254     }
255     return iEntry->second.GetPreview();
256 }
257 
258 
259 
260 
261 Bitmap BitmapCache::GetMarkedBitmap (const CacheKey& rKey)
262 {
263     ::osl::MutexGuard aGuard (maMutex);
264 
265     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
266     if (iEntry != mpBitmapContainer->end())
267     {
268         iEntry->second.SetAccessTime(mnCurrentAccessTime++);
269         return iEntry->second.GetMarkedPreview();
270     }
271     else
272         return Bitmap();
273 }
274 
275 
276 
277 
278 void BitmapCache::ReleaseBitmap (const CacheKey& rKey)
279 {
280     ::osl::MutexGuard aGuard (maMutex);
281 
282     CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
283     if (aIterator != mpBitmapContainer->end())
284     {
285         UpdateCacheSize(aIterator->second, REMOVE);
286         mpBitmapContainer->erase(aIterator);
287     }
288 }
289 
290 
291 
292 
293 bool BitmapCache::InvalidateBitmap (const CacheKey& rKey)
294 {
295     ::osl::MutexGuard aGuard (maMutex);
296 
297     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
298     if (iEntry != mpBitmapContainer->end())
299     {
300         iEntry->second.SetUpToDate(false);
301 
302         // When there is a preview then we release the replacement.  The
303         // preview itself is kept until a new one is created.
304         if (iEntry->second.HasPreview())
305         {
306             UpdateCacheSize(iEntry->second, REMOVE);
307             iEntry->second.Invalidate();
308             UpdateCacheSize(iEntry->second, ADD);
309         }
310         return true;
311     }
312     else
313         return false;
314 }
315 
316 
317 
318 
319 void BitmapCache::InvalidateCache (void)
320 {
321     ::osl::MutexGuard aGuard (maMutex);
322 
323     CacheBitmapContainer::iterator iEntry;
324     for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry)
325     {
326         iEntry->second.Invalidate();
327     }
328     ReCalculateTotalCacheSize();
329 }
330 
331 
332 
333 
334 void BitmapCache::SetBitmap (
335     const CacheKey& rKey,
336     const Bitmap& rPreview,
337     bool bIsPrecious)
338 {
339     ::osl::MutexGuard aGuard (maMutex);
340 
341     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
342     if (iEntry != mpBitmapContainer->end())
343     {
344         UpdateCacheSize(iEntry->second, REMOVE);
345         iEntry->second.SetPreview(rPreview);
346         iEntry->second.SetUpToDate(true);
347         iEntry->second.SetAccessTime(mnCurrentAccessTime++);
348     }
349     else
350     {
351         iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
352             rKey,
353             CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious))
354             ).first;
355     }
356 
357     if (iEntry != mpBitmapContainer->end())
358         UpdateCacheSize(iEntry->second, ADD);
359 }
360 
361 
362 
363 
364 void BitmapCache::SetMarkedBitmap (
365     const CacheKey& rKey,
366     const Bitmap& rPreview)
367 {
368     ::osl::MutexGuard aGuard (maMutex);
369 
370     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
371     if (iEntry != mpBitmapContainer->end())
372     {
373         UpdateCacheSize(iEntry->second, REMOVE);
374         iEntry->second.SetMarkedPreview(rPreview);
375         iEntry->second.SetAccessTime(mnCurrentAccessTime++);
376         UpdateCacheSize(iEntry->second, ADD);
377     }
378 }
379 
380 
381 
382 
383 void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious)
384 {
385     ::osl::MutexGuard aGuard (maMutex);
386 
387     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
388     if (iEntry != mpBitmapContainer->end())
389     {
390         if (iEntry->second.IsPrecious() != bIsPrecious)
391         {
392             UpdateCacheSize(iEntry->second, REMOVE);
393             iEntry->second.SetPrecious(bIsPrecious);
394             UpdateCacheSize(iEntry->second, ADD);
395         }
396     }
397     else if (bIsPrecious)
398     {
399         iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
400             rKey,
401             CacheEntry(Bitmap(), mnCurrentAccessTime++, bIsPrecious))
402             ).first;
403         UpdateCacheSize(iEntry->second, ADD);
404     }
405 }
406 
407 
408 
409 
410 void BitmapCache::ReCalculateTotalCacheSize (void)
411 {
412     ::osl::MutexGuard aGuard (maMutex);
413 
414     mnNormalCacheSize = 0;
415     mnPreciousCacheSize = 0;
416     CacheBitmapContainer::iterator iEntry;
417     for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end();  ++iEntry)
418     {
419         if (iEntry->second.IsPrecious())
420             mnPreciousCacheSize += iEntry->second.GetMemorySize();
421         else
422             mnNormalCacheSize += iEntry->second.GetMemorySize();
423     }
424     mbIsFull = (mnNormalCacheSize  >= mnMaximalNormalCacheSize);
425 
426 #ifdef VERBOSE
427     OSL_TRACE("cache size is %d/%d", mnNormalCacheSize, mnPreciousCacheSize);
428 #endif
429 }
430 
431 
432 
433 
434 void BitmapCache::Recycle (const BitmapCache& rCache)
435 {
436     ::osl::MutexGuard aGuard (maMutex);
437 
438     CacheBitmapContainer::const_iterator iOtherEntry;
439     for (iOtherEntry=rCache.mpBitmapContainer->begin();
440          iOtherEntry!=rCache.mpBitmapContainer->end();
441          ++iOtherEntry)
442     {
443         CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(iOtherEntry->first));
444         if (iEntry == mpBitmapContainer->end())
445         {
446             iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
447                 iOtherEntry->first,
448                 CacheEntry(mnCurrentAccessTime++, true))
449                 ).first;
450             UpdateCacheSize(iEntry->second, ADD);
451         }
452         if (iEntry != mpBitmapContainer->end())
453         {
454             UpdateCacheSize(iEntry->second, REMOVE);
455             iEntry->second.Recycle(iOtherEntry->second);
456             UpdateCacheSize(iEntry->second, ADD);
457         }
458     }
459 }
460 
461 
462 
463 
464 ::std::auto_ptr<BitmapCache::CacheIndex> BitmapCache::GetCacheIndex (
465     bool bIncludePrecious,
466     bool bIncludeNoPreview) const
467 {
468     ::osl::MutexGuard aGuard (maMutex);
469 
470     // Create a copy of the bitmap container.
471     SortableBitmapContainer aSortedContainer;
472     aSortedContainer.reserve(mpBitmapContainer->size());
473 
474     // Copy the relevant entries.
475     CacheBitmapContainer::iterator iEntry;
476     for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry)
477     {
478         if ( ! bIncludePrecious && iEntry->second.IsPrecious())
479             continue;
480 
481         if ( ! bIncludeNoPreview && ! iEntry->second.HasPreview())
482             continue;
483 
484         aSortedContainer.push_back(SortableBitmapContainer::value_type(
485             iEntry->first,iEntry->second));
486     }
487 
488     // Sort the remaining entries.
489     ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());
490 
491     // Return a list with the keys of the sorted entries.
492     ::std::auto_ptr<CacheIndex> pIndex(new CacheIndex());
493     SortableBitmapContainer::iterator iIndexEntry;
494     pIndex->reserve(aSortedContainer.size());
495     for (iIndexEntry=aSortedContainer.begin(); iIndexEntry!=aSortedContainer.end(); ++iIndexEntry)
496         pIndex->push_back(iIndexEntry->first);
497     return pIndex;
498 }
499 
500 
501 
502 
503 void BitmapCache::Compress (
504     const CacheKey& rKey,
505     const ::boost::shared_ptr<BitmapCompressor>& rpCompressor)
506 {
507     ::osl::MutexGuard aGuard (maMutex);
508 
509     CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
510     if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview())
511     {
512         UpdateCacheSize(iEntry->second, REMOVE);
513         iEntry->second.Compress(rpCompressor);
514         UpdateCacheSize(iEntry->second, ADD);
515     }
516 }
517 
518 
519 
520 
521 void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation)
522 {
523     sal_Int32 nEntrySize (rEntry.GetMemorySize());
524     sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize);
525     switch (eOperation)
526     {
527         case ADD:
528             rCacheSize += nEntrySize;
529             if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize)
530             {
531                 mbIsFull = true;
532 #ifdef VERBOSE
533                 OSL_TRACE("cache size is %d > %d", mnNormalCacheSize,mnMaximalNormalCacheSize);
534 #endif
535                 mpCacheCompactor->RequestCompaction();
536             }
537             break;
538 
539         case REMOVE:
540             rCacheSize -= nEntrySize;
541             if (mnNormalCacheSize < mnMaximalNormalCacheSize)
542                 mbIsFull = false;
543             break;
544 
545         default:
546             OSL_ASSERT(false);
547             break;
548     }
549 }
550 
551 
552 
553 
554 //===== CacheEntry ============================================================
555 
556 BitmapCache::CacheEntry::CacheEntry(
557     sal_Int32 nLastAccessTime,
558     bool bIsPrecious)
559     : maPreview(),
560       maMarkedPreview(),
561       mbIsUpToDate(true),
562       mnLastAccessTime(nLastAccessTime),
563       mbIsPrecious(bIsPrecious)
564 {
565 }
566 
567 
568 
569 
570 BitmapCache::CacheEntry::CacheEntry(
571     const Bitmap& rPreview,
572     sal_Int32 nLastAccessTime,
573     bool bIsPrecious)
574     : maPreview(rPreview),
575       maMarkedPreview(),
576       mbIsUpToDate(true),
577       mnLastAccessTime(nLastAccessTime),
578       mbIsPrecious(bIsPrecious)
579 {
580 }
581 
582 
583 
584 
585 inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry)
586 {
587     if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
588         && ! (HasPreview() || HasLosslessReplacement()))
589     {
590         maPreview = rEntry.maPreview;
591         maMarkedPreview = rEntry.maMarkedPreview;
592         mpReplacement = rEntry.mpReplacement;
593         mpCompressor = rEntry.mpCompressor;
594         mnLastAccessTime = rEntry.mnLastAccessTime;
595         mbIsUpToDate = rEntry.mbIsUpToDate;
596     }
597 }
598 
599 
600 
601 
602 inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize (void) const
603 {
604     sal_Int32 nSize (0);
605     nSize += maPreview.GetSizeBytes();
606     nSize += maMarkedPreview.GetSizeBytes();
607     if (mpReplacement.get() != NULL)
608         nSize += mpReplacement->GetMemorySize();
609     return nSize;
610 }
611 
612 
613 
614 
615 void BitmapCache::CacheEntry::Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor)
616 {
617     if ( ! maPreview.IsEmpty())
618     {
619         if (mpReplacement.get() == NULL)
620         {
621             mpReplacement = rpCompressor->Compress(maPreview);
622 
623 #ifdef VERBOSE
624             sal_uInt32 nOldSize (maPreview.GetSizeBytes());
625             sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0);
626             if (nOldSize == 0)
627                 nOldSize = 1;
628             sal_Int32 nRatio (100L * nNewSize / nOldSize);
629             OSL_TRACE("compressing bitmap for %x from %d to %d bytes (%d%%)",
630                 this,
631                 nOldSize,
632                 nNewSize,
633                 nRatio);
634 #endif
635 
636             mpCompressor = rpCompressor;
637         }
638 
639         maPreview.SetEmpty();
640         maMarkedPreview.SetEmpty();
641     }
642 }
643 
644 
645 
646 
647 inline void BitmapCache::CacheEntry::Decompress (void)
648 {
649     if (mpReplacement.get()!=NULL && mpCompressor.get()!=NULL && maPreview.IsEmpty())
650     {
651         maPreview = mpCompressor->Decompress(*mpReplacement);
652         maMarkedPreview.SetEmpty();
653         if ( ! mpCompressor->IsLossless())
654             mbIsUpToDate = false;
655     }
656 }
657 
658 
659 
660 inline void BitmapCache::CacheEntry::SetPreview (const Bitmap& rPreview)
661 {
662     maPreview = rPreview;
663     maMarkedPreview.SetEmpty();
664     mpReplacement.reset();
665     mpCompressor.reset();
666 }
667 
668 
669 
670 
671 bool BitmapCache::CacheEntry::HasPreview (void) const
672 {
673     return ! maPreview.IsEmpty();
674 }
675 
676 
677 
678 
679 inline void BitmapCache::CacheEntry::SetMarkedPreview (const Bitmap& rMarkedPreview)
680 {
681     maMarkedPreview = rMarkedPreview;
682 }
683 
684 
685 
686 
687 bool BitmapCache::CacheEntry::HasMarkedPreview (void) const
688 {
689     return ! maMarkedPreview.IsEmpty();
690 }
691 
692 
693 
694 
695 inline bool BitmapCache::CacheEntry::HasLosslessReplacement (void) const
696 {
697     return mpReplacement.get()!=NULL
698         && mpCompressor.get()!=NULL
699         && mpCompressor->IsLossless();
700 }
701 
702 
703 } } } // end of namespace ::sd::slidesorter::cache
704