diff options
Diffstat (limited to 'sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx')
-rwxr-xr-x | sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx new file mode 100755 index 000000000000..1bbecbef3d66 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx @@ -0,0 +1,631 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sd.hxx" + +#include "SlsBitmapCache.hxx" +#include "SlsCacheCompactor.hxx" +#include "SlsBitmapCompressor.hxx" +#include "SlsCacheConfiguration.hxx" + +#include "taskpane/SlideSorterCacheDisplay.hxx" +#include "sdpage.hxx" +#include "drawdoc.hxx" + +// Uncomment the following define for some more OSL_TRACE messages. +#ifdef DEBUG +//#define VERBOSE +#endif + +// Define the default value for the maximal cache size that is used for +// previews that are currently not visible. The visible previews are all +// held in memory at all times. This default is used only when the +// configuration does not have a value. +static const sal_Int32 MAXIMAL_CACHE_SIZE = 4L*1024L*1024L; + +using namespace ::com::sun::star::uno; + +namespace sd { namespace slidesorter { namespace cache { + +class BitmapCache::CacheEntry +{ +public: + CacheEntry(const ::boost::shared_ptr<BitmapEx>& rpBitmap, + sal_Int32 nLastAccessTime, bool bIsPrecious); + CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious); + ~CacheEntry (void) {}; + inline void Recycle (const CacheEntry& rEntry); + inline sal_Int32 GetMemorySize (void) const; + void Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor); + inline void Decompress (void); + + bool IsUpToDate (void) const { return mbIsUpToDate; } + void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; } + sal_Int32 GetAccessTime (void) const { return mnLastAccessTime; } + void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; } + ::boost::shared_ptr<BitmapEx> GetPreview (void) const { return mpPreview; } + inline void SetPreview (const ::boost::shared_ptr<BitmapEx>& rpPreview); + bool HasPreview (void) const; + bool HasReplacement (void) const { return (mpReplacement.get() != NULL); } + inline bool HasLosslessReplacement (void) const; + void Clear (void) { mpPreview.reset(); mpReplacement.reset(); mpCompressor.reset(); } + void Invalidate (void) { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; } + bool IsPrecious (void) const { return mbIsPrecious; } + void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; } + +private: + ::boost::shared_ptr<BitmapEx> mpPreview; + ::boost::shared_ptr<BitmapReplacement> mpReplacement; + ::boost::shared_ptr<BitmapCompressor> mpCompressor; + Size maBitmapSize; + bool mbIsUpToDate; + sal_Int32 mnLastAccessTime; + // When this flag is set then the bitmap is not modified by a cache + // compactor. + bool mbIsPrecious; +}; +class CacheEntry; + +class CacheHash { +public: + size_t operator()(const BitmapCache::CacheKey& p) const + { return (size_t)p; } +}; + +class BitmapCache::CacheBitmapContainer + : public ::std::hash_map<CacheKey, CacheEntry, CacheHash> +{ +public: + CacheBitmapContainer (void) {} +}; + +namespace { + +typedef ::std::vector< + ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey, + ::sd::slidesorter::cache::BitmapCache::CacheEntry> + > SortableBitmapContainer; + + /** Compare elements of the bitmap cache according to their last access + time. + */ + class AccessTimeComparator + { + public: + bool operator () ( + const SortableBitmapContainer::value_type& e1, + const SortableBitmapContainer::value_type& e2) + { + return e1.second.GetAccessTime() < e2.second.GetAccessTime(); + } + }; + + +} // end of anonymous namespace + + +//===== BitmapCache ========================================================= + +BitmapCache::BitmapCache (const sal_Int32 nMaximalNormalCacheSize) + : maMutex(), + mpBitmapContainer(new CacheBitmapContainer()), + mnNormalCacheSize(0), + mnPreciousCacheSize(0), + mnCurrentAccessTime(0), + mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE), + mpCacheCompactor(), + mbIsFull(false) +{ + if (nMaximalNormalCacheSize > 0) + mnMaximalNormalCacheSize = nMaximalNormalCacheSize; + else + { + Any aCacheSize (CacheConfiguration::Instance()->GetValue( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CacheSize")))); + if (aCacheSize.has<sal_Int32>()) + aCacheSize >>= mnMaximalNormalCacheSize; + } + + mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize); +} + + + + +BitmapCache::~BitmapCache (void) +{ + Clear(); +} + + + + +void BitmapCache::Clear (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + mpBitmapContainer->clear(); + mnNormalCacheSize = 0; + mnPreciousCacheSize = 0; + mnCurrentAccessTime = 0; +} + + + + +bool BitmapCache::IsFull (void) const +{ + return mbIsFull; +} + + + + +sal_Int32 BitmapCache::GetSize (void) +{ + return mnNormalCacheSize; +} + + + + +bool BitmapCache::HasBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + return (iEntry != mpBitmapContainer->end() + && (iEntry->second.HasPreview() || iEntry->second.HasReplacement())); +} + + + + +bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + bool bIsUpToDate = false; + CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); + if (aIterator != mpBitmapContainer->end()) + bIsUpToDate = aIterator->second.IsUpToDate(); + + return bIsUpToDate; +} + + + + +::boost::shared_ptr<BitmapEx> BitmapCache::GetBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry == mpBitmapContainer->end()) + { + // Create an empty bitmap for the given key that acts as placeholder + // until we are given the real one. Mark it as not being up to date. + SetBitmap (rKey, ::boost::shared_ptr<BitmapEx>(new BitmapEx()), false); + iEntry = mpBitmapContainer->find(rKey); + iEntry->second.SetUpToDate(false); + SSCD_SET_UPTODATE(iEntry->first,false); + } + else + { + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + + // Maybe we have to decompress the preview. + if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Decompress(); + UpdateCacheSize(iEntry->second, ADD); + } + } + return iEntry->second.GetPreview(); +} + + + + +void BitmapCache::InvalidateBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + iEntry->second.SetUpToDate(false); + SSCD_SET_UPTODATE(iEntry->first,false); + + // When there is a preview then we release the replacement. The + // preview itself is kept until a new one is created. + if (iEntry->second.HasPreview()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Invalidate(); + SSCD_SET_UPTODATE(iEntry->first,false); + UpdateCacheSize(iEntry->second, ADD); + } + } +} + + + + +void BitmapCache::InvalidateCache (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry; + for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry) + { + iEntry->second.Invalidate(); + SSCD_SET_UPTODATE(iEntry->first,false); + } + ReCalculateTotalCacheSize(); +} + + + + +void BitmapCache::SetBitmap ( + const CacheKey& rKey, + const ::boost::shared_ptr<BitmapEx>& rpPreview, + bool bIsPrecious) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetPreview(rpPreview); + iEntry->second.SetUpToDate(true); + SSCD_SET_UPTODATE(iEntry->first,true); + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + } + else + { + iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type ( + rKey, + CacheEntry (rpPreview, mnCurrentAccessTime++, bIsPrecious)) + ).first; + } + + if (iEntry != mpBitmapContainer->end()) + UpdateCacheSize(iEntry->second, ADD); +} + + + + +void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + if (iEntry->second.IsPrecious() != bIsPrecious) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetPrecious(bIsPrecious); + UpdateCacheSize(iEntry->second, ADD); + } + } + else if (bIsPrecious) + { + iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type ( + rKey, + CacheEntry ( + ::boost::shared_ptr<BitmapEx>(), + mnCurrentAccessTime++, bIsPrecious)) + ).first; + UpdateCacheSize(iEntry->second, ADD); + } +} + + + + +void BitmapCache::ReCalculateTotalCacheSize (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + mnNormalCacheSize = 0; + mnPreciousCacheSize = 0; + CacheBitmapContainer::iterator iEntry; + for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry) + { + if (iEntry->second.IsPrecious()) + mnPreciousCacheSize += iEntry->second.GetMemorySize(); + else + mnNormalCacheSize += iEntry->second.GetMemorySize(); + } + mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize); + +#ifdef VERBOSE + OSL_TRACE("cache size is %d/%d", mnNormalCacheSize, mnPreciousCacheSize); +#endif +} + + + + +void BitmapCache::Recycle (const BitmapCache& rCache) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::const_iterator iOtherEntry; + for (iOtherEntry=rCache.mpBitmapContainer->begin(); + iOtherEntry!=rCache.mpBitmapContainer->end(); + ++iOtherEntry) + { + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(iOtherEntry->first)); + if (iEntry == mpBitmapContainer->end()) + { + iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type ( + iOtherEntry->first, + CacheEntry(mnCurrentAccessTime++, true)) + ).first; + UpdateCacheSize(iEntry->second, ADD); + } + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Recycle(iOtherEntry->second); + UpdateCacheSize(iEntry->second, ADD); + } + } +} + + + + +::std::auto_ptr<BitmapCache::CacheIndex> BitmapCache::GetCacheIndex ( + bool bIncludePrecious, + bool bIncludeNoPreview) const +{ + ::osl::MutexGuard aGuard (maMutex); + + // Create a copy of the bitmap container. + SortableBitmapContainer aSortedContainer; + aSortedContainer.reserve(mpBitmapContainer->size()); + + // Copy the relevant entries. + CacheBitmapContainer::iterator iEntry; + for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry) + { + if ( ! bIncludePrecious && iEntry->second.IsPrecious()) + continue; + + if ( ! bIncludeNoPreview && ! iEntry->second.HasPreview()) + continue; + + aSortedContainer.push_back(SortableBitmapContainer::value_type( + iEntry->first,iEntry->second)); + } + + // Sort the remaining entries. + ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator()); + + // Return a list with the keys of the sorted entries. + ::std::auto_ptr<CacheIndex> pIndex(new CacheIndex()); + SortableBitmapContainer::iterator iIndexEntry; + pIndex->reserve(aSortedContainer.size()); + for (iIndexEntry=aSortedContainer.begin(); iIndexEntry!=aSortedContainer.end(); ++iIndexEntry) + pIndex->push_back(iIndexEntry->first); + return pIndex; +} + + + + +void BitmapCache::Compress ( + const CacheKey& rKey, + const ::boost::shared_ptr<BitmapCompressor>& rpCompressor) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Compress(rpCompressor); + UpdateCacheSize(iEntry->second, ADD); + } +} + + + + +void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation) +{ + sal_Int32 nEntrySize (rEntry.GetMemorySize()); + sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize); + switch (eOperation) + { + case ADD: + rCacheSize += nEntrySize; + if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize) + { + mbIsFull = true; +#ifdef VERBOSE + OSL_TRACE("cache size is %d > %d", mnNormalCacheSize,mnMaximalNormalCacheSize); +#endif + mpCacheCompactor->RequestCompaction(); + } + break; + + case REMOVE: + rCacheSize -= nEntrySize; + if (mnNormalCacheSize < mnMaximalNormalCacheSize) + mbIsFull = false; + break; + + default: + OSL_ASSERT(false); + break; + } +} + + + + +//===== CacheEntry ============================================================ + +BitmapCache::CacheEntry::CacheEntry( + sal_Int32 nLastAccessTime, + bool bIsPrecious) + : mpPreview(), + mbIsUpToDate(true), + mnLastAccessTime(nLastAccessTime), + mbIsPrecious(bIsPrecious) +{ +} + + + + +BitmapCache::CacheEntry::CacheEntry( + const ::boost::shared_ptr<BitmapEx>& rpPreview, + sal_Int32 nLastAccessTime, + bool bIsPrecious) + : mpPreview(rpPreview), + mbIsUpToDate(true), + mnLastAccessTime(nLastAccessTime), + mbIsPrecious(bIsPrecious) +{ +} + + + + +inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry) +{ + if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement()) + && ! (HasPreview() || HasLosslessReplacement())) + { + mpPreview = rEntry.mpPreview; + mpReplacement = rEntry.mpReplacement; + mpCompressor = rEntry.mpCompressor; + mnLastAccessTime = rEntry.mnLastAccessTime; + mbIsUpToDate = rEntry.mbIsUpToDate; + } +} + + + + +inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize (void) const +{ + sal_Int32 nSize (0); + if (mpPreview.get() != NULL) + nSize += mpPreview->GetSizeBytes(); + if (mpReplacement.get() != NULL) + nSize += mpReplacement->GetMemorySize(); + return nSize; +} + + + + +void BitmapCache::CacheEntry::Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor) +{ + if (mpPreview.get() != NULL) + { + if (mpReplacement.get() == NULL) + { + mpReplacement = rpCompressor->Compress(mpPreview); + +#ifdef VERBOSE + sal_uInt32 nOldSize (mpPreview->GetSizeBytes()); + sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0); + if (nOldSize == 0) + nOldSize = 1; + sal_Int32 nRatio (100L * nNewSize / nOldSize); + OSL_TRACE("compressing bitmap for %x from %d to %d bytes (%d%%)", + this, + nOldSize, + nNewSize, + nRatio); +#endif + + mpCompressor = rpCompressor; + } + + mpPreview.reset(); + } +} + + + + +inline void BitmapCache::CacheEntry::Decompress (void) +{ + if (mpReplacement.get()!=NULL && mpCompressor.get()!=NULL && mpPreview.get()==NULL) + { + mpPreview = mpCompressor->Decompress(*mpReplacement); + if ( ! mpCompressor->IsLossless()) + mbIsUpToDate = false; + } +} + + + +inline void BitmapCache::CacheEntry::SetPreview (const ::boost::shared_ptr<BitmapEx>& rpPreview) +{ + mpPreview = rpPreview; + mpReplacement.reset(); + mpCompressor.reset(); +} + + + + +bool BitmapCache::CacheEntry::HasPreview (void) const +{ + if (mpPreview.get() != NULL) + return mpPreview->GetSizePixel().Width()>0 && mpPreview->GetSizePixel().Height()>0; + else + return false; +} + + + + +inline bool BitmapCache::CacheEntry::HasLosslessReplacement (void) const +{ + return mpReplacement.get()!=NULL + && mpCompressor.get()!=NULL + && mpCompressor->IsLossless(); +} + + +} } } // end of namespace ::sd::slidesorter::cache |