blob: 17d3f148579f0efb2ff6f2e686f4ba7de170fd2c [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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 "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 Bitmap& rBitmap, 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; }
Bitmap GetPreview (void) const { return maPreview; }
inline void SetPreview (const Bitmap& rPreview);
bool HasPreview (void) const;
Bitmap GetMarkedPreview (void) const { return maMarkedPreview; }
inline void SetMarkedPreview (const Bitmap& rMarkePreview);
bool HasMarkedPreview (void) const;
bool HasReplacement (void) const { return (mpReplacement.get() != NULL); }
inline bool HasLosslessReplacement (void) const;
void Clear (void) { maPreview.SetEmpty(); maMarkedPreview.SetEmpty();
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:
Bitmap maPreview;
Bitmap maMarkedPreview;
::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;
}
Bitmap 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, Bitmap(), false);
iEntry = mpBitmapContainer->find(rKey);
iEntry->second.SetUpToDate(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();
}
Bitmap BitmapCache::GetMarkedBitmap (const CacheKey& rKey)
{
::osl::MutexGuard aGuard (maMutex);
CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
if (iEntry != mpBitmapContainer->end())
{
iEntry->second.SetAccessTime(mnCurrentAccessTime++);
return iEntry->second.GetMarkedPreview();
}
else
return Bitmap();
}
void BitmapCache::ReleaseBitmap (const CacheKey& rKey)
{
::osl::MutexGuard aGuard (maMutex);
CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
if (aIterator != mpBitmapContainer->end())
{
UpdateCacheSize(aIterator->second, REMOVE);
mpBitmapContainer->erase(aIterator);
}
}
bool BitmapCache::InvalidateBitmap (const CacheKey& rKey)
{
::osl::MutexGuard aGuard (maMutex);
CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
if (iEntry != mpBitmapContainer->end())
{
iEntry->second.SetUpToDate(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();
UpdateCacheSize(iEntry->second, ADD);
}
return true;
}
else
return false;
}
void BitmapCache::InvalidateCache (void)
{
::osl::MutexGuard aGuard (maMutex);
CacheBitmapContainer::iterator iEntry;
for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry)
{
iEntry->second.Invalidate();
}
ReCalculateTotalCacheSize();
}
void BitmapCache::SetBitmap (
const CacheKey& rKey,
const Bitmap& rPreview,
bool bIsPrecious)
{
::osl::MutexGuard aGuard (maMutex);
CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
if (iEntry != mpBitmapContainer->end())
{
UpdateCacheSize(iEntry->second, REMOVE);
iEntry->second.SetPreview(rPreview);
iEntry->second.SetUpToDate(true);
iEntry->second.SetAccessTime(mnCurrentAccessTime++);
}
else
{
iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type (
rKey,
CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious))
).first;
}
if (iEntry != mpBitmapContainer->end())
UpdateCacheSize(iEntry->second, ADD);
}
void BitmapCache::SetMarkedBitmap (
const CacheKey& rKey,
const Bitmap& rPreview)
{
::osl::MutexGuard aGuard (maMutex);
CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
if (iEntry != mpBitmapContainer->end())
{
UpdateCacheSize(iEntry->second, REMOVE);
iEntry->second.SetMarkedPreview(rPreview);
iEntry->second.SetAccessTime(mnCurrentAccessTime++);
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(Bitmap(), 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)
: maPreview(),
maMarkedPreview(),
mbIsUpToDate(true),
mnLastAccessTime(nLastAccessTime),
mbIsPrecious(bIsPrecious)
{
}
BitmapCache::CacheEntry::CacheEntry(
const Bitmap& rPreview,
sal_Int32 nLastAccessTime,
bool bIsPrecious)
: maPreview(rPreview),
maMarkedPreview(),
mbIsUpToDate(true),
mnLastAccessTime(nLastAccessTime),
mbIsPrecious(bIsPrecious)
{
}
inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry)
{
if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
&& ! (HasPreview() || HasLosslessReplacement()))
{
maPreview = rEntry.maPreview;
maMarkedPreview = rEntry.maMarkedPreview;
mpReplacement = rEntry.mpReplacement;
mpCompressor = rEntry.mpCompressor;
mnLastAccessTime = rEntry.mnLastAccessTime;
mbIsUpToDate = rEntry.mbIsUpToDate;
}
}
inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize (void) const
{
sal_Int32 nSize (0);
nSize += maPreview.GetSizeBytes();
nSize += maMarkedPreview.GetSizeBytes();
if (mpReplacement.get() != NULL)
nSize += mpReplacement->GetMemorySize();
return nSize;
}
void BitmapCache::CacheEntry::Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor)
{
if ( ! maPreview.IsEmpty())
{
if (mpReplacement.get() == NULL)
{
mpReplacement = rpCompressor->Compress(maPreview);
#ifdef VERBOSE
sal_uInt32 nOldSize (maPreview.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;
}
maPreview.SetEmpty();
maMarkedPreview.SetEmpty();
}
}
inline void BitmapCache::CacheEntry::Decompress (void)
{
if (mpReplacement.get()!=NULL && mpCompressor.get()!=NULL && maPreview.IsEmpty())
{
maPreview = mpCompressor->Decompress(*mpReplacement);
maMarkedPreview.SetEmpty();
if ( ! mpCompressor->IsLossless())
mbIsUpToDate = false;
}
}
inline void BitmapCache::CacheEntry::SetPreview (const Bitmap& rPreview)
{
maPreview = rPreview;
maMarkedPreview.SetEmpty();
mpReplacement.reset();
mpCompressor.reset();
}
bool BitmapCache::CacheEntry::HasPreview (void) const
{
return ! maPreview.IsEmpty();
}
inline void BitmapCache::CacheEntry::SetMarkedPreview (const Bitmap& rMarkedPreview)
{
maMarkedPreview = rMarkedPreview;
}
bool BitmapCache::CacheEntry::HasMarkedPreview (void) const
{
return ! maMarkedPreview.IsEmpty();
}
inline bool BitmapCache::CacheEntry::HasLosslessReplacement (void) const
{
return mpReplacement.get()!=NULL
&& mpCompressor.get()!=NULL
&& mpCompressor->IsLossless();
}
} } } // end of namespace ::sd::slidesorter::cache