summaryrefslogtreecommitdiffstats
path: root/sd/source/ui/slidesorter/cache
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sd/source/ui/slidesorter/cache
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sd/source/ui/slidesorter/cache')
-rw-r--r--sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx550
-rw-r--r--sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx208
-rw-r--r--sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx197
-rw-r--r--sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx138
-rw-r--r--sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx71
-rw-r--r--sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx46
-rw-r--r--sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx189
-rw-r--r--sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx87
-rw-r--r--sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx144
-rw-r--r--sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx68
-rw-r--r--sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx278
-rw-r--r--sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx152
-rw-r--r--sd/source/ui/slidesorter/cache/SlsPageCache.cxx109
-rw-r--r--sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx420
-rw-r--r--sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx176
-rw-r--r--sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx98
-rw-r--r--sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx50
-rw-r--r--sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx36
-rw-r--r--sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx44
-rw-r--r--sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx275
-rw-r--r--sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx122
21 files changed, 3458 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx
new file mode 100644
index 000000000..87c727408
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx
@@ -0,0 +1,550 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <unordered_map>
+#include "SlsBitmapCache.hxx"
+#include "SlsCacheCompactor.hxx"
+#include "SlsBitmapCompressor.hxx"
+#include "SlsCacheConfiguration.hxx"
+
+#include <sal/log.hxx>
+
+// 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.
+const sal_Int32 MAXIMAL_CACHE_SIZE = 4*1024L*1024L;
+
+using namespace ::com::sun::star::uno;
+
+namespace sd::slidesorter::cache {
+
+class BitmapCache::CacheEntry
+{
+public:
+ CacheEntry(const BitmapEx& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious);
+ CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious);
+ inline void Recycle (const CacheEntry& rEntry);
+ inline sal_Int32 GetMemorySize() const;
+ void Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor);
+ inline void Decompress();
+
+ bool IsUpToDate() const { return mbIsUpToDate; }
+ void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; }
+ sal_Int32 GetAccessTime() const { return mnLastAccessTime; }
+ void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; }
+
+ const BitmapEx& GetPreview() const { return maPreview; }
+ inline void SetPreview (const BitmapEx& rPreview);
+ bool HasPreview() const;
+
+ const BitmapEx& GetMarkedPreview() const { return maMarkedPreview; }
+ inline void SetMarkedPreview (const BitmapEx& rMarkePreview);
+
+ bool HasReplacement() const { return (mpReplacement != nullptr); }
+ inline bool HasLosslessReplacement() const;
+ void Invalidate() { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; }
+ bool IsPrecious() const { return mbIsPrecious; }
+ void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; }
+
+private:
+ BitmapEx maPreview;
+ BitmapEx maMarkedPreview;
+ std::shared_ptr<BitmapReplacement> mpReplacement;
+ std::shared_ptr<BitmapCompressor> mpCompressor;
+ bool mbIsUpToDate;
+ sal_Int32 mnLastAccessTime;
+ // When this flag is set then the bitmap is not modified by a cache
+ // compactor.
+ bool mbIsPrecious;
+};
+
+namespace {
+
+class CacheHash {
+public:
+ size_t operator()(const BitmapCache::CacheKey& p) const
+ { return reinterpret_cast<size_t>(p); }
+};
+
+}
+
+class BitmapCache::CacheBitmapContainer
+ : public std::unordered_map<CacheKey, CacheEntry, CacheHash>
+{
+public:
+ CacheBitmapContainer() {}
+};
+
+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 ()
+ : mpBitmapContainer(new CacheBitmapContainer()),
+ mnNormalCacheSize(0),
+ mnPreciousCacheSize(0),
+ mnCurrentAccessTime(0),
+ mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
+ mbIsFull(false)
+{
+ Any aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize"));
+ if (aCacheSize.has<sal_Int32>())
+ aCacheSize >>= mnMaximalNormalCacheSize;
+
+ mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize);
+}
+
+BitmapCache::~BitmapCache()
+{
+ Clear();
+}
+
+void BitmapCache::Clear()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ mpBitmapContainer->clear();
+ mnNormalCacheSize = 0;
+ mnPreciousCacheSize = 0;
+ mnCurrentAccessTime = 0;
+}
+
+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;
+}
+
+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, BitmapEx(), 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();
+}
+
+BitmapEx 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 BitmapEx();
+}
+
+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()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ for (auto& rEntry : *mpBitmapContainer)
+ {
+ rEntry.second.Invalidate();
+ }
+ ReCalculateTotalCacheSize();
+}
+
+void BitmapCache::SetBitmap (
+ const CacheKey& rKey,
+ const BitmapEx& 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->emplace(
+ rKey,
+ CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious)
+ ).first;
+ }
+
+ if (iEntry != mpBitmapContainer->end())
+ UpdateCacheSize(iEntry->second, ADD);
+}
+
+void BitmapCache::SetMarkedBitmap (
+ const CacheKey& rKey,
+ const BitmapEx& 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->emplace(
+ rKey,
+ CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious)
+ ).first;
+ UpdateCacheSize(iEntry->second, ADD);
+ }
+}
+
+void BitmapCache::ReCalculateTotalCacheSize()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ mnNormalCacheSize = 0;
+ mnPreciousCacheSize = 0;
+ for (const auto& rEntry : *mpBitmapContainer)
+ {
+ if (rEntry.second.IsPrecious())
+ mnPreciousCacheSize += rEntry.second.GetMemorySize();
+ else
+ mnNormalCacheSize += rEntry.second.GetMemorySize();
+ }
+ mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize);
+
+ SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize);
+}
+
+void BitmapCache::Recycle (const BitmapCache& rCache)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ for (const auto& rOtherEntry : *rCache.mpBitmapContainer)
+ {
+ CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first));
+ if (iEntry == mpBitmapContainer->end())
+ {
+ iEntry = mpBitmapContainer->emplace(
+ rOtherEntry.first,
+ CacheEntry(mnCurrentAccessTime++, true)
+ ).first;
+ UpdateCacheSize(iEntry->second, ADD);
+ }
+ if (iEntry != mpBitmapContainer->end())
+ {
+ UpdateCacheSize(iEntry->second, REMOVE);
+ iEntry->second.Recycle(rOtherEntry.second);
+ UpdateCacheSize(iEntry->second, ADD);
+ }
+ }
+}
+
+BitmapCache::CacheIndex BitmapCache::GetCacheIndex() const
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ // Create a copy of the bitmap container.
+ SortableBitmapContainer aSortedContainer;
+ aSortedContainer.reserve(mpBitmapContainer->size());
+
+ // Copy the relevant entries.
+ for (const auto& rEntry : *mpBitmapContainer)
+ {
+ if ( rEntry.second.IsPrecious())
+ continue;
+
+ if ( ! rEntry.second.HasPreview())
+ continue;
+
+ aSortedContainer.emplace_back(rEntry.first, rEntry.second);
+ }
+
+ // Sort the remaining entries.
+ ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());
+
+ // Return a list with the keys of the sorted entries.
+ CacheIndex aIndex;
+ aIndex.reserve(aSortedContainer.size());
+ for (const auto& rIndexEntry : aSortedContainer)
+ aIndex.push_back(rIndexEntry.first);
+ return aIndex;
+}
+
+void BitmapCache::Compress (
+ const CacheKey& rKey,
+ const std::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;
+ SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize);
+ mpCacheCompactor->RequestCompaction();
+ }
+ break;
+
+ case REMOVE:
+ rCacheSize -= nEntrySize;
+ if (mnNormalCacheSize < mnMaximalNormalCacheSize)
+ mbIsFull = false;
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+}
+
+//===== CacheEntry ============================================================
+
+BitmapCache::CacheEntry::CacheEntry(
+ sal_Int32 nLastAccessTime,
+ bool bIsPrecious)
+ : mbIsUpToDate(true),
+ mnLastAccessTime(nLastAccessTime),
+ mbIsPrecious(bIsPrecious)
+{
+}
+
+BitmapCache::CacheEntry::CacheEntry(
+ const BitmapEx& rPreview,
+ sal_Int32 nLastAccessTime,
+ bool bIsPrecious)
+ : maPreview(rPreview),
+ 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() const
+{
+ sal_Int32 nSize (0);
+ nSize += maPreview.GetSizeBytes();
+ nSize += maMarkedPreview.GetSizeBytes();
+ if (mpReplacement != nullptr)
+ nSize += mpReplacement->GetMemorySize();
+ return nSize;
+}
+
+void BitmapCache::CacheEntry::Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor)
+{
+ if ( maPreview.IsEmpty())
+ return;
+
+ if (mpReplacement == nullptr)
+ {
+ mpReplacement = rpCompressor->Compress(maPreview);
+
+#ifdef DEBUG_SD_SLSBITMAPCACHE
+ 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);
+ SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)");
+#endif
+
+ mpCompressor = rpCompressor;
+ }
+
+ maPreview.SetEmpty();
+ maMarkedPreview.SetEmpty();
+}
+
+inline void BitmapCache::CacheEntry::Decompress()
+{
+ if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty())
+ {
+ maPreview = mpCompressor->Decompress(*mpReplacement);
+ maMarkedPreview.SetEmpty();
+ if ( ! mpCompressor->IsLossless())
+ mbIsUpToDate = false;
+ }
+}
+
+inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx& rPreview)
+{
+ maPreview = rPreview;
+ maMarkedPreview.SetEmpty();
+ mpReplacement.reset();
+ mpCompressor.reset();
+}
+
+bool BitmapCache::CacheEntry::HasPreview() const
+{
+ return ! maPreview.IsEmpty();
+}
+
+inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview)
+{
+ maMarkedPreview = rMarkedPreview;
+}
+
+inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const
+{
+ return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless();
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx
new file mode 100644
index 000000000..98b0bcb53
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <vcl/bitmapex.hxx>
+#include <osl/mutex.hxx>
+#include <memory>
+
+class SdrPage;
+
+namespace sd::slidesorter::cache
+{
+class CacheCompactor;
+class BitmapCompressor;
+
+/** This low level cache is the actual bitmap container. It supports a
+ precious flag for every preview bitmap and keeps track of total sizes
+ for all previews with/without this flag. The precious flag is used by
+ compaction algorithms to determine which previews may be compressed or
+ even discarded and which have to remain in their original form. The
+ precious flag is usually set for the visible previews.
+
+ Additionally to the actual preview there is an optional marked preview.
+ This is used for slides excluded from the slide show which have a preview
+ that shows a mark (some sort of bitmap overlay) to that effect.
+*/
+class BitmapCache
+{
+public:
+ /** The key for looking up preview bitmaps is a pointer to an SdrPage
+ object. The prior use of PageObjectViewObjectContact objects (which
+ ultimately use them) turned out to be less suitable because their
+ life time is shorter then that of the page objects. Frequent
+ destruction and re-creation of the preview bitmaps was the result.
+ */
+ typedef const SdrPage* CacheKey;
+ class CacheEntry;
+ class CacheBitmapContainer;
+ typedef ::std::vector<CacheKey> CacheIndex;
+
+ /** Create a new cache for bitmap objects.
+ The default value from the configuration is used.
+ When that does not exist then an internal default value is
+ used.
+ */
+ explicit BitmapCache();
+
+ /** The destructor clears the cache and releases all bitmaps still in it.
+ */
+ ~BitmapCache();
+
+ /** Remove all preview bitmaps from the cache. After this call the
+ cache is empty.
+ */
+ void Clear();
+
+ /** Return <TRUE/> when the cache is full, i.e. the cache compactor had
+ to be run.
+ */
+ bool IsFull() const { return mbIsFull; }
+
+ /** Return the memory size that is occupied by all non-precious bitmaps
+ in the cache.
+ */
+ sal_Int32 GetSize() const { return mnNormalCacheSize; }
+
+ /** Return <TRUE/> when a preview bitmap exists for the given key.
+ */
+ bool HasBitmap(const CacheKey& rKey);
+
+ /** Return <TRUE/> when a preview bitmap exists for the given key and
+ when it is up-to-date.
+ */
+ bool BitmapIsUpToDate(const CacheKey& rKey);
+
+ /** Return the preview bitmap for the given contact object.
+ */
+ BitmapEx GetBitmap(const CacheKey& rKey);
+
+ /** Return the marked preview bitmap for the given contact object.
+ */
+ BitmapEx GetMarkedBitmap(const CacheKey& rKey);
+
+ /** Release the reference to the preview bitmap that is associated with
+ the given key.
+ */
+ void ReleaseBitmap(const CacheKey& rKey);
+
+ /** Mark the specified preview bitmap as not being up-to-date
+ anymore.
+ @return
+ When the key references a page in the cache then
+ return <TRUE/>. When the key is not known then <FALSE/>
+ is returned.
+ */
+ bool InvalidateBitmap(const CacheKey& rKey);
+
+ /** Mark all preview bitmaps as not being up-to-date anymore.
+ */
+ void InvalidateCache();
+
+ /** Add or replace a bitmap for the given key.
+ */
+ void SetBitmap(const CacheKey& rKey, const BitmapEx& rPreview, bool bIsPrecious);
+
+ /** Add or replace a marked bitmap for the given key.
+ */
+ void SetMarkedBitmap(const CacheKey& rKey, const BitmapEx& rPreview);
+
+ /** Mark the specified preview bitmap as precious, i.e. that it must not
+ be compressed or otherwise removed from the cache.
+ */
+ void SetPrecious(const CacheKey& rKey, bool bIsPrecious);
+
+ /** Calculate the cache size. This should rarely be necessary because
+ the cache size is tracked with each modification of preview
+ bitmaps.
+ */
+ void ReCalculateTotalCacheSize();
+
+ /** Use the previews in the given cache to initialize missing previews.
+ */
+ void Recycle(const BitmapCache& rCache);
+
+ /** Return a list of sorted cache keys that represent an index into (a
+ part of) the cache. The entries of the index are sorted according
+ to last access times with the least recently access time first.
+ Entries with the precious flag set are omitted.
+ Entries with that have no preview bitmaps are omitted.
+ */
+ CacheIndex GetCacheIndex() const;
+
+ /** Compress the specified preview bitmap with the given bitmap
+ compressor. A reference to the compressor is stored for later
+ decompression.
+ */
+ void Compress(const CacheKey& rKey, const std::shared_ptr<BitmapCompressor>& rpCompressor);
+
+private:
+ mutable ::osl::Mutex maMutex;
+
+ std::unique_ptr<CacheBitmapContainer> mpBitmapContainer;
+
+ /** Total size of bytes that are occupied by bitmaps in the cache for
+ whom the slides are currently not inside the visible area.
+ */
+ sal_Int32 mnNormalCacheSize;
+
+ /** Total size of bytes that are occupied by bitmaps in the cache for
+ whom the slides are currently visible.
+ */
+ sal_Int32 mnPreciousCacheSize;
+
+ /** At the moment the access time is not an actual time or date value
+ but a counter that is increased with every access. It thus defines
+ the same ordering as a true time.
+ */
+ sal_Int32 mnCurrentAccessTime;
+
+ /** The maximal cache size for the off-screen preview bitmaps. When
+ mnNormalCacheSize grows larger than this value then the
+ mpCacheCompactor member is used to reduce the cache size.
+ */
+ sal_Int32 mnMaximalNormalCacheSize;
+
+ /** The cache compactor is used to reduce the number of bytes used by
+ off-screen preview bitmaps.
+ */
+ ::std::unique_ptr<CacheCompactor> mpCacheCompactor;
+
+ /** This flag stores if the cache is or recently was full, i.e. the
+ cache compactor has or had to be run in order to reduce the cache
+ size to the allowed value.
+ */
+ bool mbIsFull;
+
+ /** Update mnNormalCacheSize or mnPreciousCacheSize according to the
+ precious flag of the specified preview bitmap and the specified
+ operation.
+ */
+ enum CacheOperation
+ {
+ ADD,
+ REMOVE
+ };
+ void UpdateCacheSize(const CacheEntry& rKey, CacheOperation eOperation);
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx
new file mode 100644
index 000000000..d4da935dd
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsBitmapCompressor.hxx"
+
+#include <tools/stream.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/pngwrite.hxx>
+
+namespace sd::slidesorter::cache {
+
+//===== NoBitmapCompression ===================================================
+
+/** This dummy replacement simply stores a shared pointer to the original
+ preview bitmap.
+*/
+class NoBitmapCompression::DummyReplacement
+ : public BitmapReplacement
+{
+public:
+ BitmapEx maPreview;
+
+ explicit DummyReplacement (const BitmapEx& rPreview) : maPreview(rPreview) { }
+ virtual ~DummyReplacement() {}
+ virtual sal_Int32 GetMemorySize() const override { return maPreview.GetSizeBytes(); }
+};
+
+std::shared_ptr<BitmapReplacement> NoBitmapCompression::Compress (const BitmapEx& rBitmap) const
+{
+ return std::make_shared<DummyReplacement>(rBitmap);
+}
+
+BitmapEx NoBitmapCompression::Decompress (const BitmapReplacement& rBitmapData) const
+{
+ return dynamic_cast<const DummyReplacement&>(rBitmapData).maPreview;
+}
+
+bool NoBitmapCompression::IsLossless() const
+{
+ return true;
+}
+
+//===== CompressionByDeletion =================================================
+
+std::shared_ptr<BitmapReplacement> CompressionByDeletion::Compress (const BitmapEx& ) const
+{
+ return std::shared_ptr<BitmapReplacement>();
+}
+
+BitmapEx CompressionByDeletion::Decompress (const BitmapReplacement& ) const
+{
+ // Return a NULL pointer. This will eventually lead to a request for
+ // the creation of a new one.
+ return BitmapEx();
+}
+
+bool CompressionByDeletion::IsLossless() const
+{
+ return false;
+}
+
+//===== ResolutionReduction ===================================================
+
+/** Store a scaled down bitmap together with the original size.
+*/
+class ResolutionReduction::ResolutionReducedReplacement : public BitmapReplacement
+{
+public:
+ BitmapEx maPreview;
+ Size maOriginalSize;
+
+ virtual ~ResolutionReducedReplacement();
+ virtual sal_Int32 GetMemorySize() const override;
+};
+
+ResolutionReduction::ResolutionReducedReplacement::~ResolutionReducedReplacement()
+{
+}
+
+sal_Int32 ResolutionReduction::ResolutionReducedReplacement::GetMemorySize() const
+{
+ return maPreview.GetSizeBytes();
+}
+
+std::shared_ptr<BitmapReplacement> ResolutionReduction::Compress (
+ const BitmapEx& rBitmap) const
+{
+ auto pResult = std::make_shared<ResolutionReducedReplacement>();
+ pResult->maPreview = rBitmap;
+ Size aSize (rBitmap.GetSizePixel());
+ pResult->maOriginalSize = aSize;
+ if (aSize.Width()>0 && aSize.Width()<mnWidth)
+ {
+ int nHeight = aSize.Height() * mnWidth / aSize.Width() ;
+ pResult->maPreview.Scale(Size(mnWidth,nHeight));
+ }
+
+ return pResult;
+}
+
+BitmapEx ResolutionReduction::Decompress (const BitmapReplacement& rBitmapData) const
+{
+ BitmapEx aResult;
+
+ const ResolutionReducedReplacement* pData (
+ dynamic_cast<const ResolutionReducedReplacement*>(&rBitmapData));
+
+ if ( pData && ! pData->maPreview.IsEmpty())
+ {
+ aResult = pData->maPreview;
+ if (pData->maOriginalSize.Width() > mnWidth)
+ aResult.Scale(pData->maOriginalSize);
+ }
+
+ return aResult;
+}
+
+bool ResolutionReduction::IsLossless() const
+{
+ return false;
+}
+
+//===== PNGCompression ========================================================
+
+class PngCompression::PngReplacement : public BitmapReplacement
+{
+public:
+ void* mpData;
+ sal_Int32 mnDataSize;
+ PngReplacement()
+ : mpData(nullptr),
+ mnDataSize(0)
+ {}
+ virtual ~PngReplacement()
+ {
+ delete [] static_cast<char*>(mpData);
+ }
+ virtual sal_Int32 GetMemorySize() const override
+ {
+ return mnDataSize;
+ }
+};
+
+std::shared_ptr<BitmapReplacement> PngCompression::Compress (const BitmapEx& rBitmap) const
+{
+ vcl::PNGWriter aWriter(rBitmap);
+ SvMemoryStream aStream (32768, 32768);
+ aWriter.Write(aStream);
+
+ auto pResult = std::make_shared<PngReplacement>();
+ pResult->mnDataSize = aStream.Tell();
+ pResult->mpData = new char[pResult->mnDataSize];
+ memcpy(pResult->mpData, aStream.GetData(), pResult->mnDataSize);
+
+ return pResult;
+}
+
+BitmapEx PngCompression::Decompress (
+ const BitmapReplacement& rBitmapData) const
+{
+ BitmapEx aResult;
+ const PngReplacement* pData (dynamic_cast<const PngReplacement*>(&rBitmapData));
+ if (pData != nullptr)
+ {
+ SvMemoryStream aStream (pData->mpData, pData->mnDataSize, StreamMode::READ);
+ vcl::PngImageReader aReader (aStream);
+ aResult = aReader.read().GetBitmap();
+ }
+
+ return aResult;
+}
+
+bool PngCompression::IsLossless() const
+{
+ return true;
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx
new file mode 100644
index 000000000..4754bead9
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <memory>
+
+class BitmapEx;
+
+namespace sd::slidesorter::cache
+{
+class BitmapReplacement;
+
+/** This interface class provides the minimal method set for classes that
+ implement the compression and decompression of preview bitmaps.
+*/
+class BitmapCompressor
+{
+public:
+ /** Compress the given bitmap into a replacement format that is specific
+ to the compressor class.
+ */
+ virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rBitmap) const = 0;
+
+ /** Decompress the given replacement data into a preview bitmap.
+ Depending on the compression technique the returned bitmap may
+ differ from the original bitmap given to the Compress() method. It
+ may even of the wrong size or empty or the NULL pointer. It is the
+ task of the caller to create a new preview bitmap if the returned
+ one is not as desired.
+ */
+ virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const = 0;
+
+ /** Return whether the compression and decompression is lossless. This
+ value is used by the caller of Decompress() to decide whether to use
+ the returned bitmap as is or if a new preview has to be created.
+ */
+ virtual bool IsLossless() const = 0;
+
+protected:
+ ~BitmapCompressor() {}
+};
+
+/** Interface for preview bitmap replacements. Each bitmap
+ compressor/decompressor has to provide an implementation that is
+ suitable to store the compressed bitmaps.
+*/
+class BitmapReplacement
+{
+public:
+ virtual sal_Int32 GetMemorySize() const { return 0; }
+
+protected:
+ ~BitmapReplacement() {}
+};
+
+/** This is one trivial bitmap compressor. It stores bitmaps unmodified
+ instead of compressing them.
+ This compressor is lossless.
+*/
+class NoBitmapCompression : public BitmapCompressor
+{
+ class DummyReplacement;
+
+public:
+ virtual ~NoBitmapCompression() {}
+ virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rpBitmap) const override;
+ virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override;
+ virtual bool IsLossless() const override;
+};
+
+/** This is another trivial bitmap compressor. Instead of compressing a
+ bitmap, it throws the bitmap away. Its Decompress() method returns a
+ NULL pointer. The caller has to create a new preview bitmap instead.
+ This compressor clearly is not lossless.
+*/
+class CompressionByDeletion : public BitmapCompressor
+{
+public:
+ virtual ~CompressionByDeletion() {}
+ virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rBitmap) const override;
+ virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override;
+ virtual bool IsLossless() const override;
+};
+
+/** Compress a preview bitmap by reducing its resolution. While the aspect
+ ratio is maintained the horizontal resolution is scaled down to 100
+ pixels.
+ This compressor is not lossless.
+*/
+class ResolutionReduction : public BitmapCompressor
+{
+ class ResolutionReducedReplacement;
+ static const sal_Int32 mnWidth = 100;
+
+public:
+ virtual ~ResolutionReduction() {}
+ virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rpBitmap) const override;
+ /** Scale the replacement bitmap up to the original size.
+ */
+ virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override;
+ virtual bool IsLossless() const override;
+};
+
+/** Compress preview bitmaps using the PNG format.
+ This compressor is lossless.
+*/
+class PngCompression : public BitmapCompressor
+{
+ class PngReplacement;
+
+public:
+ virtual ~PngCompression() {}
+ virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rBitmap) const override;
+ virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override;
+ virtual bool IsLossless() const override;
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx
new file mode 100644
index 000000000..a9182c2a2
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsBitmapFactory.hxx"
+
+#include <PreviewRenderer.hxx>
+#include <sdpage.hxx>
+#include <vcl/bitmapex.hxx>
+
+namespace sd::slidesorter::view {
+class SlideSorterView;
+class PageObjectViewObjectContact;
+}
+
+namespace sd::slidesorter::cache {
+
+BitmapFactory::BitmapFactory()
+ : maRenderer(false)
+{
+}
+
+BitmapFactory::~BitmapFactory()
+{
+}
+
+BitmapEx BitmapFactory::CreateBitmap (
+ const SdPage& rPage,
+ const Size& rPixelSize,
+ const bool bDoSuperSampling)
+{
+ Size aSize (rPixelSize);
+ if (bDoSuperSampling)
+ {
+ // Supersampling factor
+ int aSuperSamplingFactor = 2;
+ aSize.setWidth( aSize.Width() * aSuperSamplingFactor );
+ aSize.setHeight( aSize.Height() * aSuperSamplingFactor );
+ }
+
+ BitmapEx aPreview (maRenderer.RenderPage (
+ &rPage,
+ aSize,
+ true,
+ false).GetBitmapEx());
+ if (bDoSuperSampling)
+ {
+ aPreview.Scale(rPixelSize, BmpScaleFlag::BestQuality);
+ }
+
+ return aPreview;
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx
new file mode 100644
index 000000000..c1733b825
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <PreviewRenderer.hxx>
+
+class SdPage;
+class Size;
+
+namespace sd::slidesorter::cache
+{
+/** This factory class creates preview bitmaps for page objects. It is
+ merely an adapter for the PreviewRenderer.
+*/
+class BitmapFactory
+{
+public:
+ BitmapFactory();
+ ~BitmapFactory();
+
+ BitmapEx CreateBitmap(const SdPage& rPage, const Size& rPixelSize, const bool bDoSuperSampling);
+
+private:
+ PreviewRenderer maRenderer;
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx
new file mode 100644
index 000000000..79ab9fab2
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include "SlsCacheCompactor.hxx"
+
+#include "SlsBitmapCompressor.hxx"
+#include "SlsBitmapCache.hxx"
+#include "SlsCacheConfiguration.hxx"
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+/** This is a trivial implementation of the CacheCompactor interface class.
+ It ignores calls to RequestCompaction() and thus will never decrease the
+ total size of off-screen preview bitmaps.
+*/
+class NoCacheCompaction
+ : public ::sd::slidesorter::cache::CacheCompactor
+{
+public:
+ NoCacheCompaction (
+ ::sd::slidesorter::cache::BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize)
+ : CacheCompactor(rCache, nMaximalCacheSize)
+ {}
+
+ virtual void RequestCompaction() override { /* Ignored */ };
+
+protected:
+ virtual void Run() override { /* Do nothing */ };
+};
+
+/** This implementation of the CacheCompactor interface class uses one of
+ several bitmap compression algorithms to reduce the number of the bytes
+ of the off-screen previews in the bitmap cache. See the documentation
+ of CacheCompactor::Create() for more details on configuration properties
+ that control the choice of compression algorithm.
+*/
+class CacheCompactionByCompression
+ : public ::sd::slidesorter::cache::CacheCompactor
+{
+public:
+ CacheCompactionByCompression (
+ ::sd::slidesorter::cache::BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize,
+ const std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor>& rpCompressor);
+
+protected:
+ virtual void Run() override;
+
+private:
+ std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor> mpCompressor;
+};
+
+} // end of anonymous namespace
+
+namespace sd::slidesorter::cache {
+
+::std::unique_ptr<CacheCompactor> CacheCompactor::Create (
+ BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize)
+{
+ static const char sNone[] = "None";
+
+ std::shared_ptr<BitmapCompressor> pCompressor;
+ OUString sCompressionPolicy("PNGCompression");
+ Any aCompressionPolicy (CacheConfiguration::Instance()->GetValue("CompressionPolicy"));
+ if (aCompressionPolicy.has<OUString>())
+ aCompressionPolicy >>= sCompressionPolicy;
+ if (sCompressionPolicy == sNone)
+ pCompressor = std::make_shared<NoBitmapCompression>();
+ else if (sCompressionPolicy == "Erase")
+ pCompressor = std::make_shared<CompressionByDeletion>();
+ else if (sCompressionPolicy == "ResolutionReduction")
+ pCompressor = std::make_shared<ResolutionReduction>();
+ else
+ pCompressor = std::make_shared<PngCompression>();
+
+ ::std::unique_ptr<CacheCompactor> pCompactor;
+ OUString sCompactionPolicy("Compress");
+ Any aCompactionPolicy (CacheConfiguration::Instance()->GetValue("CompactionPolicy"));
+ if (aCompactionPolicy.has<OUString>())
+ aCompactionPolicy >>= sCompactionPolicy;
+ if (sCompactionPolicy == sNone)
+ pCompactor.reset(new NoCacheCompaction(rCache,nMaximalCacheSize));
+ else
+ pCompactor.reset(new CacheCompactionByCompression(rCache,nMaximalCacheSize,pCompressor));
+
+ return pCompactor;
+}
+
+void CacheCompactor::RequestCompaction()
+{
+ if ( ! mbIsCompactionRunning && ! maCompactionTimer.IsActive())
+ maCompactionTimer.Start();
+}
+
+CacheCompactor::CacheCompactor(
+ BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize)
+ : mrCache(rCache),
+ mnMaximalCacheSize(nMaximalCacheSize),
+ maCompactionTimer("sd CacheCompactor maCompactionTimer"),
+ mbIsCompactionRunning(false)
+{
+ maCompactionTimer.SetTimeout(100);
+ maCompactionTimer.SetInvokeHandler(LINK(this,CacheCompactor,CompactionCallback));
+}
+
+IMPL_LINK_NOARG(CacheCompactor, CompactionCallback, Timer *, void)
+{
+ mbIsCompactionRunning = true;
+
+ try
+ {
+ Run();
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ mbIsCompactionRunning = false;
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+namespace {
+
+//===== CacheCompactionByCompression ==========================================
+
+CacheCompactionByCompression::CacheCompactionByCompression (
+ ::sd::slidesorter::cache::BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize,
+ const std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor>& rpCompressor)
+ : CacheCompactor(rCache,nMaximalCacheSize),
+ mpCompressor(rpCompressor)
+{
+}
+
+void CacheCompactionByCompression::Run()
+{
+ if (mrCache.GetSize() <= mnMaximalCacheSize)
+ return;
+
+ SAL_INFO("sd.sls", __func__ << ": bitmap cache uses too much space: " << mrCache.GetSize() << " > " << mnMaximalCacheSize);
+
+ ::sd::slidesorter::cache::BitmapCache::CacheIndex aIndex (
+ mrCache.GetCacheIndex());
+ for (const auto& rpIndex : aIndex)
+ {
+ if (rpIndex == nullptr)
+ continue;
+
+ mrCache.Compress(rpIndex, mpCompressor);
+ if (mrCache.GetSize() < mnMaximalCacheSize)
+ break;
+ }
+ mrCache.ReCalculateTotalCacheSize();
+ SAL_INFO("sd.sls", __func__ << ": there are now " << mrCache.GetSize() << " bytes occupied");
+}
+
+} // end of anonymous namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx
new file mode 100644
index 000000000..d694ae1a1
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <vcl/timer.hxx>
+#include <memory>
+
+namespace sd::slidesorter::cache {
+
+class BitmapCache;
+
+/** This is an interface class whose implementations are created via the
+ Create() factory method.
+*/
+class CacheCompactor
+{
+public:
+ virtual ~CacheCompactor() {};
+
+ /** Create a new instance of the CacheCompactor interface class. The
+ type of compaction algorithm used depends on values from the
+ configuration: the SlideSorter/PreviewCache/CompactionPolicy
+ property of the Impress.xcs file currently supports the values
+ "None" and "Compress". With the later the CompressionPolicy
+ property is evaluated which implementation of the BitmapCompress
+ interface class to use as bitmap compressor.
+ @param rCache
+ The bitmap cache on which to operate.
+ @param nMaximalCacheSize
+ The total number of bytes the off-screen bitmaps in the cache
+ may have. When the Run() method is (indirectly) called the
+ compactor tries to reduce that summed size of off-screen bitmaps
+ under this number. However, it is not guaranteed that this
+ works in all cases.
+ */
+ static ::std::unique_ptr<CacheCompactor> Create (
+ BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize);
+
+ /** Request a compaction of the off-screen previews in the bitmap
+ cache. This calls via a timer the Run() method.
+ */
+ virtual void RequestCompaction();
+
+protected:
+ BitmapCache& mrCache;
+ sal_Int32 mnMaximalCacheSize;
+
+ CacheCompactor(
+ BitmapCache& rCache,
+ sal_Int32 nMaximalCacheSize);
+
+ /** This method actually tries to reduce the total number of bytes used
+ by the off-screen preview bitmaps.
+ */
+ virtual void Run() = 0;
+
+private:
+ /** This timer is used to collect calls to RequestCompaction() and
+ eventually call Run().
+ */
+ Timer maCompactionTimer;
+ bool mbIsCompactionRunning;
+ DECL_LINK(CompactionCallback, Timer *, void);
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx
new file mode 100644
index 000000000..fd08c7627
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsCacheConfiguration.hxx"
+#include <vcl/svapp.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sd::slidesorter::cache {
+
+namespace
+{
+ typedef std::shared_ptr<CacheConfiguration> CacheConfigSharedPtr;
+ CacheConfigSharedPtr& theInstance()
+ {
+ static CacheConfigSharedPtr SINGLETON;
+ return SINGLETON;
+ }
+}
+
+std::weak_ptr<CacheConfiguration> CacheConfiguration::mpWeakInstance;
+
+std::shared_ptr<CacheConfiguration> CacheConfiguration::Instance()
+{
+ SolarMutexGuard aSolarGuard;
+ CacheConfigSharedPtr &rInstancePtr = theInstance();
+ if (!rInstancePtr)
+ {
+ // Maybe somebody else kept a previously created instance alive.
+ if ( ! mpWeakInstance.expired())
+ rInstancePtr = std::shared_ptr<CacheConfiguration>(mpWeakInstance);
+ if (!rInstancePtr)
+ {
+ // We have to create a new instance.
+ rInstancePtr.reset(new CacheConfiguration());
+ mpWeakInstance = rInstancePtr;
+ // Prepare to release this instance in the near future.
+ rInstancePtr->m_ReleaseTimer.SetInvokeHandler(
+ LINK(rInstancePtr.get(),CacheConfiguration,TimerCallback));
+ rInstancePtr->m_ReleaseTimer.SetTimeout(5000 /* 5s */);
+ rInstancePtr->m_ReleaseTimer.Start();
+ }
+ }
+ return rInstancePtr;
+}
+
+CacheConfiguration::CacheConfiguration()
+ : m_ReleaseTimer("sd::CacheConfiguration maReleaseTimer")
+{
+ // Get the cache size from configuration.
+ try
+ {
+ // Obtain access to the configuration.
+ Reference<lang::XMultiServiceFactory> xProvider =
+ configuration::theDefaultProvider::get( ::comphelper::getProcessComponentContext() );
+
+ // Obtain access to Impress configuration.
+ Sequence<Any> aCreationArguments(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(OUString("/org.openoffice.Office.Impress/"))},
+ {"depth", Any(sal_Int32(-1))}
+ }));
+
+ Reference<XInterface> xRoot (xProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aCreationArguments));
+ if ( ! xRoot.is())
+ return;
+ Reference<container::XHierarchicalNameAccess> xHierarchy (xRoot, UNO_QUERY);
+ if ( ! xHierarchy.is())
+ return;
+
+ // Get the node for the slide sorter preview cache.
+ mxCacheNode.set( xHierarchy->getByHierarchicalName("MultiPaneGUI/SlideSorter/PreviewCache"), UNO_QUERY);
+ }
+ catch (RuntimeException &)
+ {
+ }
+ catch (Exception &)
+ {
+ }
+}
+
+Any CacheConfiguration::GetValue (const OUString& rName)
+{
+ Any aResult;
+
+ if (mxCacheNode != nullptr)
+ {
+ try
+ {
+ aResult = mxCacheNode->getByName(rName);
+ }
+ catch (Exception &)
+ {
+ }
+ }
+
+ return aResult;
+}
+
+IMPL_STATIC_LINK_NOARG(CacheConfiguration, TimerCallback, Timer *, void)
+{
+ CacheConfigSharedPtr &rInstancePtr = theInstance();
+ // Release our reference to the instance.
+ rInstancePtr.reset();
+ // note: if there are no other references to the instance, m_ReleaseTimer
+ // will be deleted now
+}
+
+void CacheConfiguration::Shutdown()
+{
+ CacheConfigSharedPtr &rInstancePtr = theInstance();
+ rInstancePtr.reset();
+ assert(mpWeakInstance.expired()); // ensure m_ReleaseTimer is destroyed
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx
new file mode 100644
index 000000000..d53bcd713
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Any.hxx>
+#include <vcl/timer.hxx>
+#include <memory>
+
+namespace com::sun::star::container
+{
+class XNameAccess;
+}
+
+namespace sd::slidesorter::cache
+{
+/** A very simple and easy-to-use access to configuration entries regarding
+ the slide sorter cache.
+*/
+class CacheConfiguration
+{
+public:
+ /** Return an instance to this class. The reference is released after 5
+ seconds. Subsequent calls to this function will create a new
+ instance.
+ */
+ static std::shared_ptr<CacheConfiguration> Instance();
+
+ static void Shutdown();
+
+ /** Look up the specified value in
+ MultiPaneGUI/SlideSorter/PreviewCache. When the specified value
+ does not exist then an empty Any is returned.
+ */
+ css::uno::Any GetValue(const OUString& rName);
+
+private:
+ /** When a caller holds a reference after we have released ours we use
+ this weak pointer to avoid creating a new instance.
+ */
+ static std::weak_ptr<CacheConfiguration> mpWeakInstance;
+ Timer m_ReleaseTimer;
+ css::uno::Reference<css::container::XNameAccess> mxCacheNode;
+
+ CacheConfiguration();
+
+ DECL_STATIC_LINK(CacheConfiguration, TimerCallback, Timer*, void);
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx
new file mode 100644
index 000000000..6275754fa
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsGenericPageCache.hxx"
+
+#include "SlsQueueProcessor.hxx"
+#include "SlsRequestPriorityClass.hxx"
+#include "SlsRequestFactory.hxx"
+#include "SlsBitmapCache.hxx"
+#include <cache/SlsPageCacheManager.hxx>
+#include <tools/debug.hxx>
+
+namespace sd::slidesorter::cache {
+
+GenericPageCache::GenericPageCache (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling,
+ const SharedCacheContext& rpCacheContext)
+ : maRequestQueue(rpCacheContext),
+ mpCacheContext(rpCacheContext),
+ maPreviewSize(rPreviewSize),
+ mbDoSuperSampling(bDoSuperSampling)
+{
+ // A large size may indicate an error of the caller. After all we
+ // are creating previews.
+ DBG_ASSERT (maPreviewSize.Width()<1000 && maPreviewSize.Height()<1000,
+ "GenericPageCache<>::GetPreviewBitmap(): bitmap requested with large width. "
+ "This may indicate an error.");
+}
+
+GenericPageCache::~GenericPageCache()
+{
+ if (mpQueueProcessor != nullptr)
+ mpQueueProcessor->Stop();
+ maRequestQueue.Clear();
+ mpQueueProcessor.reset();
+
+ if (mpBitmapCache != nullptr)
+ PageCacheManager::Instance()->ReleaseCache(mpBitmapCache);
+ mpBitmapCache.reset();
+}
+
+void GenericPageCache::ProvideCacheAndProcessor()
+{
+ if (mpBitmapCache == nullptr)
+ mpBitmapCache = PageCacheManager::Instance()->GetCache(
+ mpCacheContext->GetModel(),
+ maPreviewSize);
+
+ if (mpQueueProcessor == nullptr)
+ mpQueueProcessor.reset(new QueueProcessor(
+ maRequestQueue,
+ mpBitmapCache,
+ maPreviewSize,
+ mbDoSuperSampling,
+ mpCacheContext));
+}
+
+void GenericPageCache::ChangePreviewSize (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling)
+{
+ if (rPreviewSize==maPreviewSize && bDoSuperSampling==mbDoSuperSampling)
+ return;
+
+ // A large size may indicate an error of the caller. After all we
+ // are creating previews.
+ DBG_ASSERT (maPreviewSize.Width()<1000 && maPreviewSize.Height()<1000,
+ "GenericPageCache<>::GetPreviewBitmap(): bitmap requested with large width. "
+ "This may indicate an error.");
+
+ if (mpBitmapCache != nullptr)
+ {
+ mpBitmapCache = PageCacheManager::Instance()->ChangeSize(
+ mpBitmapCache, maPreviewSize, rPreviewSize);
+ if (mpQueueProcessor != nullptr)
+ {
+ mpQueueProcessor->SetPreviewSize(rPreviewSize, bDoSuperSampling);
+ mpQueueProcessor->SetBitmapCache(mpBitmapCache);
+ }
+ }
+ maPreviewSize = rPreviewSize;
+ mbDoSuperSampling = bDoSuperSampling;
+}
+
+BitmapEx GenericPageCache::GetPreviewBitmap (
+ const CacheKey aKey,
+ const bool bResize)
+{
+ assert(aKey != nullptr);
+
+ BitmapEx aPreview;
+ bool bMayBeUpToDate = true;
+ ProvideCacheAndProcessor();
+ const SdrPage* pPage = mpCacheContext->GetPage(aKey);
+ if (mpBitmapCache->HasBitmap(pPage))
+ {
+ aPreview = mpBitmapCache->GetBitmap(pPage);
+ const Size aBitmapSize (aPreview.GetSizePixel());
+ if (aBitmapSize != maPreviewSize)
+ {
+ // Scale the bitmap to the desired size when that is possible,
+ // i.e. the bitmap is not empty.
+ if (bResize && !aBitmapSize.IsEmpty())
+ {
+ aPreview.Scale(maPreviewSize);
+ }
+ bMayBeUpToDate = false;
+ }
+ else
+ bMayBeUpToDate = true;
+ }
+ else
+ bMayBeUpToDate = false;
+
+ // Request the creation of a correctly sized preview bitmap. We do this
+ // even when the size of the bitmap in the cache is correct because its
+ // content may be not up-to-date anymore.
+ RequestPreviewBitmap(aKey, bMayBeUpToDate);
+
+ return aPreview;
+}
+
+BitmapEx GenericPageCache::GetMarkedPreviewBitmap (
+ const CacheKey aKey)
+{
+ assert(aKey != nullptr);
+
+ ProvideCacheAndProcessor();
+ const SdrPage* pPage = mpCacheContext->GetPage(aKey);
+ BitmapEx aMarkedPreview (mpBitmapCache->GetMarkedBitmap(pPage));
+
+ return aMarkedPreview;
+}
+
+void GenericPageCache::SetMarkedPreviewBitmap (
+ const CacheKey aKey,
+ const BitmapEx& rMarkedBitmap)
+{
+ assert(aKey != nullptr);
+
+ ProvideCacheAndProcessor();
+ const SdrPage* pPage = mpCacheContext->GetPage(aKey);
+ mpBitmapCache->SetMarkedBitmap(pPage, rMarkedBitmap);
+}
+
+void GenericPageCache::RequestPreviewBitmap (
+ const CacheKey aKey,
+ const bool bMayBeUpToDate)
+{
+ assert(aKey != nullptr);
+
+ const SdrPage* pPage = mpCacheContext->GetPage(aKey);
+
+ ProvideCacheAndProcessor();
+
+ // Determine if the available bitmap is up to date.
+ bool bIsUpToDate = false;
+ if (bMayBeUpToDate)
+ bIsUpToDate = mpBitmapCache->BitmapIsUpToDate (pPage);
+ if (bIsUpToDate)
+ {
+ const BitmapEx aPreview (mpBitmapCache->GetBitmap(pPage));
+ if (aPreview.IsEmpty() || aPreview.GetSizePixel()!=maPreviewSize)
+ bIsUpToDate = false;
+ }
+
+ if ( bIsUpToDate)
+ return;
+
+ // No, the bitmap is not up-to-date. Request a new one.
+ RequestPriorityClass ePriorityClass (NOT_VISIBLE);
+ if (mpCacheContext->IsVisible(aKey))
+ {
+ if (mpBitmapCache->HasBitmap(pPage))
+ ePriorityClass = VISIBLE_OUTDATED_PREVIEW;
+ else
+ ePriorityClass = VISIBLE_NO_PREVIEW;
+ }
+ maRequestQueue.AddRequest(aKey, ePriorityClass);
+ mpQueueProcessor->Start(ePriorityClass);
+}
+
+bool GenericPageCache::InvalidatePreviewBitmap (const CacheKey aKey)
+{
+ // Invalidate the page in all caches that reference it, not just this one.
+ std::shared_ptr<cache::PageCacheManager> pCacheManager (
+ cache::PageCacheManager::Instance());
+ if (pCacheManager)
+ return pCacheManager->InvalidatePreviewBitmap(
+ mpCacheContext->GetModel(),
+ aKey);
+ else if (mpBitmapCache != nullptr)
+ return mpBitmapCache->InvalidateBitmap(mpCacheContext->GetPage(aKey));
+ else
+ return false;
+}
+
+void GenericPageCache::InvalidateCache ()
+{
+ if (!mpBitmapCache)
+ return;
+
+ // When the cache is being invalidated then it makes no sense to
+ // continue creating preview bitmaps. However, this may be
+ // re-started below.
+ mpQueueProcessor->Stop();
+ maRequestQueue.Clear();
+
+ // Mark the previews in the cache as not being up-to-date anymore.
+ // Depending on the given bUpdateCache flag we start to create new
+ // preview bitmaps.
+ mpBitmapCache->InvalidateCache();
+ RequestFactory()(maRequestQueue, mpCacheContext);
+}
+
+void GenericPageCache::SetPreciousFlag (
+ const CacheKey aKey,
+ const bool bIsPrecious)
+{
+ ProvideCacheAndProcessor();
+
+ // Change the request priority class according to the new precious flag.
+ if (bIsPrecious)
+ {
+ if (mpBitmapCache->HasBitmap(mpCacheContext->GetPage(aKey)))
+ maRequestQueue.ChangeClass(aKey,VISIBLE_OUTDATED_PREVIEW);
+ else
+ maRequestQueue.ChangeClass(aKey,VISIBLE_NO_PREVIEW);
+ }
+ else
+ {
+ if (mpBitmapCache->IsFull())
+ {
+ // When the bitmap cache is full then requests for slides that
+ // are not visible are removed.
+ maRequestQueue.RemoveRequest(aKey);
+ }
+ else
+ maRequestQueue.ChangeClass(aKey,NOT_VISIBLE);
+ }
+
+ mpBitmapCache->SetPrecious(mpCacheContext->GetPage(aKey), bIsPrecious);
+}
+
+void GenericPageCache::Pause()
+{
+ ProvideCacheAndProcessor();
+ if (mpQueueProcessor != nullptr)
+ mpQueueProcessor->Pause();
+}
+
+void GenericPageCache::Resume()
+{
+ ProvideCacheAndProcessor();
+ if (mpQueueProcessor != nullptr)
+ mpQueueProcessor->Resume();
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx
new file mode 100644
index 000000000..900d40268
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "SlsRequestQueue.hxx"
+#include <memory>
+
+#include <vcl/bitmapex.hxx>
+
+namespace sd::slidesorter::cache {
+
+class BitmapCache;
+class QueueProcessor;
+
+/** This basically is the implementation class for the PageCache class.
+*/
+class GenericPageCache
+{
+public:
+ /** The page cache is created with a reference to the SlideSorter and
+ thus has access to both view and model. This allows the cache to
+ fill itself with requests for all pages or just the visible ones.
+ @param rPreviewSize
+ The size of the previews is expected in pixel values.
+ @param bDoSuperSampling
+ When <TRUE/> the previews are rendered larger and then scaled
+ down to the requested size to improve image quality.
+ */
+ GenericPageCache (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling,
+ const SharedCacheContext& rpCacheContext);
+
+ ~GenericPageCache();
+
+ /** Change the size of the preview bitmaps. This may be caused by a
+ resize of the slide sorter window or a change of the number of
+ columns.
+ */
+ void ChangePreviewSize (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling);
+
+ /** Request a preview bitmap for the specified page object in the
+ specified size. The returned bitmap may be a preview of the preview,
+ i.e. either a scaled (up or down) version of a previous preview (of
+ the wrong size) or an empty bitmap. In this case a request for the
+ generation of a new preview is created and inserted into the request
+ queue. When the preview is available the page shape will be told to
+ paint itself again. When it then calls this method again if
+ receives the correctly sized preview bitmap.
+ @param rRequestData
+ This data is used to determine the preview.
+ @param bResize
+ When <TRUE/> then when the available bitmap has not the
+ requested size, it is scaled before it is returned. When
+ <FALSE/> then the bitmap is returned in the wrong size and it is
+ the task of the caller to scale it.
+ @return
+ Returns a bitmap that is either empty, contains a scaled (up or
+ down) version or is the requested bitmap.
+ */
+ BitmapEx GetPreviewBitmap (
+ const CacheKey aKey,
+ const bool bResize);
+ BitmapEx GetMarkedPreviewBitmap (
+ const CacheKey aKey);
+ void SetMarkedPreviewBitmap (
+ const CacheKey aKey,
+ const BitmapEx& rMarkedBitmap);
+
+ /** When the requested preview bitmap does not yet exist or is not
+ up-to-date then the rendering of one is scheduled. Otherwise this
+ method does nothing.
+ @param rRequestData
+ This data is used to determine the preview.
+ @param bMayBeUpToDate
+ This flag helps the method to determine whether an existing
+ preview that matches the request is up to date. If the caller
+ knows that it is not then by passing <FALSE/> he tells us that we
+ do not have to check the up-to-date flag a second time. If
+ unsure use <TRUE/>.
+ */
+ void RequestPreviewBitmap (
+ const CacheKey aKey,
+ const bool bMayBeUpToDate);
+
+ /** Tell the cache to replace the bitmap associated with the given
+ request data with a new one that reflects recent changes in the
+ content of the page object.
+ @return
+ When the key is known then return <TRUE/>.
+ */
+ bool InvalidatePreviewBitmap (const CacheKey aKey);
+
+ /** Call this method when all preview bitmaps have to be generated anew.
+ This is the case when the size of the page objects on the screen has
+ changed or when the model has changed.
+ */
+ void InvalidateCache ();
+
+ /** With the precious flag you can control whether a bitmap can be
+ removed from the cache or reduced in size to make room for other
+ bitmaps or is so precious that it will not be touched. A typical
+ use is to set the precious flag for the visible pages.
+ */
+ void SetPreciousFlag (const CacheKey aKey, const bool bIsPrecious);
+
+ void Pause();
+ void Resume();
+
+private:
+ std::shared_ptr<BitmapCache> mpBitmapCache;
+
+ RequestQueue maRequestQueue;
+
+ std::unique_ptr<QueueProcessor> mpQueueProcessor;
+
+ SharedCacheContext mpCacheContext;
+
+ /** The current size of preview bitmaps.
+ */
+ Size maPreviewSize;
+
+ bool mbDoSuperSampling;
+
+ /** Both bitmap cache and queue processor are created on demand by this
+ method.
+ */
+ void ProvideCacheAndProcessor();
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsPageCache.cxx b/sd/source/ui/slidesorter/cache/SlsPageCache.cxx
new file mode 100644
index 000000000..82b1b8ae4
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsPageCache.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tools/gen.hxx>
+#include "SlsGenericPageCache.hxx"
+#include <cache/SlsPageCache.hxx>
+
+using namespace ::com::sun::star;
+
+namespace sd::slidesorter::cache {
+
+//===== PageCache =============================================================
+
+PageCache::PageCache (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling,
+ const SharedCacheContext& rpCacheContext)
+ : mpImplementation(
+ new GenericPageCache(
+ rPreviewSize,
+ bDoSuperSampling,
+ rpCacheContext))
+{
+}
+
+PageCache::~PageCache()
+{
+}
+
+void PageCache::ChangeSize (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling)
+{
+ mpImplementation->ChangePreviewSize(rPreviewSize, bDoSuperSampling);
+}
+
+BitmapEx PageCache::GetPreviewBitmap (
+ const CacheKey aKey,
+ const bool bResize)
+{
+ return mpImplementation->GetPreviewBitmap(aKey, bResize);
+}
+
+BitmapEx PageCache::GetMarkedPreviewBitmap (
+ const CacheKey aKey)
+{
+ return mpImplementation->GetMarkedPreviewBitmap(aKey);
+}
+
+void PageCache::SetMarkedPreviewBitmap (
+ const CacheKey aKey,
+ const BitmapEx& rMarkedBitmap)
+{
+ mpImplementation->SetMarkedPreviewBitmap(aKey, rMarkedBitmap);
+}
+
+void PageCache::RequestPreviewBitmap (const CacheKey aKey)
+{
+ return mpImplementation->RequestPreviewBitmap(aKey, true);
+}
+
+void PageCache::InvalidatePreviewBitmap (
+ const CacheKey aKey)
+{
+ if (mpImplementation->InvalidatePreviewBitmap(aKey))
+ RequestPreviewBitmap(aKey);
+}
+
+void PageCache::InvalidateCache()
+{
+ mpImplementation->InvalidateCache();
+}
+
+void PageCache::SetPreciousFlag (
+ const CacheKey aKey,
+ const bool bIsPrecious)
+{
+ mpImplementation->SetPreciousFlag(aKey, bIsPrecious);
+}
+
+void PageCache::Pause()
+{
+ mpImplementation->Pause();
+}
+
+void PageCache::Resume()
+{
+ mpImplementation->Resume();
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx b/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx
new file mode 100644
index 000000000..45afd93c9
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <cache/SlsPageCacheManager.hxx>
+
+#include "SlsBitmapCache.hxx"
+
+#include <deque>
+#include <map>
+#include <memory>
+#include <unordered_map>
+
+namespace {
+
+/** Collection of data that is stored for all active preview caches.
+*/
+class CacheDescriptor
+{
+public:
+ ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
+ Size maPreviewSize;
+
+ CacheDescriptor(
+ ::sd::slidesorter::cache::PageCacheManager::DocumentKey const & pDocument,
+ const Size& rPreviewSize)
+ :mpDocument(pDocument),maPreviewSize(rPreviewSize)
+ {}
+ /// Test for equality with respect to all members.
+ class Equal {public: bool operator() (
+ const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
+ return rDescriptor1.mpDocument==rDescriptor2.mpDocument
+ && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
+ } };
+ /// Hash function that takes all members into account.
+ class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
+ return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width();
+ } };
+};
+
+/** Collection of data that is stored for the inactive, recently used
+ caches.
+*/
+class RecentlyUsedCacheDescriptor
+{
+public:
+ Size maPreviewSize;
+ std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache;
+
+ RecentlyUsedCacheDescriptor(
+ const Size& rPreviewSize,
+ const std::shared_ptr< ::sd::slidesorter::cache::BitmapCache>& rpCache)
+ :maPreviewSize(rPreviewSize),mpCache(rpCache)
+ {}
+};
+
+/** The list of recently used caches is organized as queue. When elements
+ are added the list is shortened to the maximally allowed number of
+ elements by removing the least recently used elements.
+*/
+typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
+
+/** Compare the caches by preview size. Those that match the given size
+ come first, then, regardless of the given size, the largest ones before
+ the smaller ones.
+*/
+class BestFittingCacheComparer
+{
+public:
+ explicit BestFittingCacheComparer (const Size& rPreferredSize)
+ : maPreferredSize(rPreferredSize)
+ {}
+ bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
+ const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
+ {
+ if (rElement2.first == maPreferredSize)
+ return false;
+ else if (rElement1.first == maPreferredSize)
+ return true;
+ else
+ return (rElement1.first.Width()*rElement1.first.Height()
+ > rElement2.first.Width()*rElement2.first.Height());
+ }
+
+private:
+ Size maPreferredSize;
+};
+
+} // end of anonymous namespace
+
+namespace sd::slidesorter::cache {
+
+/** Container for the active caches.
+*/
+class PageCacheManager::PageCacheContainer
+ : public std::unordered_map<CacheDescriptor,
+ std::shared_ptr<BitmapCache>,
+ CacheDescriptor::Hash,
+ CacheDescriptor::Equal>
+{
+public:
+ PageCacheContainer() {}
+
+ /** Compare entries in the cache container with respect to the cache
+ address only.
+ */
+ class CompareWithCache { public:
+ explicit CompareWithCache(const std::shared_ptr<BitmapCache>& rpCache)
+ : mpCache(rpCache) {}
+ bool operator () (const PageCacheContainer::value_type& rValue) const
+ { return rValue.second == mpCache; }
+ private:
+ std::shared_ptr<BitmapCache> mpCache;
+ };
+};
+
+/** The recently used caches are stored in one queue for each document.
+*/
+class PageCacheManager::RecentlyUsedPageCaches
+{
+public:
+ typedef DocumentKey key_type;
+ typedef RecentlyUsedQueue mapped_type;
+ typedef std::map<key_type,mapped_type>::iterator iterator;
+private:
+ std::map<key_type,mapped_type> maMap;
+public:
+ RecentlyUsedPageCaches () {};
+
+ iterator end() { return maMap.end(); }
+ void clear() { maMap.clear(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+};
+
+class PageCacheManager::Deleter
+{
+public:
+ void operator() (PageCacheManager* pObject) { delete pObject; }
+};
+
+//===== PageCacheManager ====================================================
+
+std::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
+
+std::shared_ptr<PageCacheManager> PageCacheManager::Instance()
+{
+ std::shared_ptr<PageCacheManager> pInstance;
+
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+
+ pInstance = mpInstance.lock();
+ if (pInstance == nullptr)
+ {
+ pInstance = std::shared_ptr<PageCacheManager>(
+ new PageCacheManager(),
+ PageCacheManager::Deleter());
+ mpInstance = pInstance;
+ }
+
+ return pInstance;
+}
+
+PageCacheManager::PageCacheManager()
+ : mpPageCaches(new PageCacheContainer()),
+ mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches())
+{
+}
+
+PageCacheManager::~PageCacheManager()
+{
+}
+
+std::shared_ptr<BitmapCache> PageCacheManager::GetCache (
+ const DocumentKey& pDocument,
+ const Size& rPreviewSize)
+{
+ std::shared_ptr<BitmapCache> pResult;
+
+ // Look for the cache in the list of active caches.
+ CacheDescriptor aKey (pDocument, rPreviewSize);
+ PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
+ if (iCache != mpPageCaches->end())
+ pResult = iCache->second;
+
+ // Look for the cache in the list of recently used caches.
+ if (pResult == nullptr)
+ pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
+
+ // Create the cache when no suitable one does exist.
+ if (pResult == nullptr)
+ pResult = std::make_shared<BitmapCache>();
+
+ // The cache may be newly created and thus empty or is old and may
+ // contain previews that are not up-to-date. Recycle previews from
+ // other caches to fill in the holes.
+ Recycle(pResult, pDocument,rPreviewSize);
+
+ // Put the new (or old) cache into the container.
+ mpPageCaches->emplace(aKey, pResult);
+
+ return pResult;
+}
+
+void PageCacheManager::Recycle (
+ const std::shared_ptr<BitmapCache>& rpCache,
+ const DocumentKey& pDocument,
+ const Size& rPreviewSize)
+{
+ BestFittingPageCaches aCaches;
+
+ // Add bitmap caches from active caches.
+ for (auto& rActiveCache : *mpPageCaches)
+ {
+ if (rActiveCache.first.mpDocument == pDocument)
+ aCaches.emplace_back(
+ rActiveCache.first.maPreviewSize, rActiveCache.second);
+ }
+
+ // Add bitmap caches from recently used caches.
+ RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
+ if (iQueue != mpRecentlyUsedPageCaches->end())
+ {
+ for (const auto& rRecentCache : iQueue->second)
+ aCaches.emplace_back(
+ rRecentCache.maPreviewSize, rRecentCache.mpCache);
+ }
+
+ ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
+
+ for (const auto& rBestCache : aCaches)
+ {
+ rpCache->Recycle(*rBestCache.second);
+ }
+}
+
+void PageCacheManager::ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache)
+{
+ PageCacheContainer::iterator iCache (::std::find_if(
+ mpPageCaches->begin(),
+ mpPageCaches->end(),
+ PageCacheContainer::CompareWithCache(rpCache)));
+
+ if (iCache != mpPageCaches->end())
+ {
+ assert(iCache->second == rpCache);
+
+ PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
+
+ mpPageCaches->erase(iCache);
+ }
+}
+
+std::shared_ptr<BitmapCache> PageCacheManager::ChangeSize (
+ const std::shared_ptr<BitmapCache>& rpCache,
+ const Size&,
+ const Size& rNewPreviewSize)
+{
+ std::shared_ptr<BitmapCache> pResult;
+
+ if (rpCache != nullptr)
+ {
+ // Look up the given cache in the list of active caches.
+ PageCacheContainer::iterator iCacheToChange (::std::find_if(
+ mpPageCaches->begin(),
+ mpPageCaches->end(),
+ PageCacheContainer::CompareWithCache(rpCache)));
+ if (iCacheToChange != mpPageCaches->end())
+ {
+ assert(iCacheToChange->second == rpCache);
+
+ // Now, we can change the preview size of the existing one by
+ // removing the cache from the list and re-insert it with the
+ // updated size.
+ const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
+ iCacheToChange->first.mpDocument);
+ mpPageCaches->erase(iCacheToChange);
+ mpPageCaches->emplace(
+ CacheDescriptor(aKey,rNewPreviewSize),
+ rpCache);
+
+ pResult = rpCache;
+ }
+ else
+ {
+ assert(iCacheToChange != mpPageCaches->end());
+ }
+ }
+
+ return pResult;
+}
+
+bool PageCacheManager::InvalidatePreviewBitmap (
+ const DocumentKey& pDocument,
+ const SdrPage* pKey)
+{
+ bool bHasChanged (false);
+
+ if (pDocument!=nullptr)
+ {
+ // Iterate over all caches that are currently in use and invalidate
+ // the previews in those that belong to the document.
+ for (auto& rCache : *mpPageCaches)
+ if (rCache.first.mpDocument == pDocument)
+ bHasChanged |= rCache.second->InvalidateBitmap(pKey);
+
+ // Invalidate the previews in the recently used caches belonging to
+ // the given document.
+ RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
+ if (iQueue != mpRecentlyUsedPageCaches->end())
+ {
+ for (const auto& rCache2 : iQueue->second)
+ bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey);
+ }
+ }
+
+ return bHasChanged;
+}
+
+void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey& pDocument)
+{
+ if (pDocument == nullptr)
+ return;
+
+ // Iterate over all caches that are currently in use and invalidate the
+ // previews in those that belong to the document.
+ for (auto& rCache : *mpPageCaches)
+ if (rCache.first.mpDocument == pDocument)
+ rCache.second->InvalidateCache();
+
+ // Invalidate the previews in the recently used caches belonging to the
+ // given document.
+ RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
+ if (iQueue != mpRecentlyUsedPageCaches->end())
+ {
+ for (const auto& rCache2 : iQueue->second)
+ rCache2.mpCache->InvalidateCache();
+ }
+}
+
+void PageCacheManager::InvalidateAllCaches()
+{
+ // Iterate over all caches that are currently in use and invalidate
+ // them.
+ for (auto& rCache : *mpPageCaches)
+ rCache.second->InvalidateCache();
+
+ // Remove all recently used caches, there is not much sense in storing
+ // invalidated and unused caches.
+ mpRecentlyUsedPageCaches->clear();
+}
+
+void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage)
+{
+ for (auto& rCache : *mpPageCaches)
+ rCache.second->ReleaseBitmap(pPage);
+}
+
+std::shared_ptr<BitmapCache> PageCacheManager::GetRecentlyUsedCache (
+ const DocumentKey& pDocument,
+ const Size& rPreviewSize)
+{
+ std::shared_ptr<BitmapCache> pCache;
+
+ // Look for the cache in the list of recently used caches.
+ RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
+ if (iQueue != mpRecentlyUsedPageCaches->end())
+ {
+ RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(),
+ [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; });
+ if (iCache != iQueue->second.end())
+ {
+ pCache = iCache->mpCache;
+ iQueue->second.erase(iCache);
+ }
+ }
+
+ return pCache;
+}
+
+void PageCacheManager::PutRecentlyUsedCache(
+ DocumentKey const & pDocument,
+ const Size& rPreviewSize,
+ const std::shared_ptr<BitmapCache>& rpCache)
+{
+ // Look up the list of recently used caches for the given document.
+ RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
+ if (iQueue == mpRecentlyUsedPageCaches->end())
+ iQueue = mpRecentlyUsedPageCaches->emplace(
+ pDocument, RecentlyUsedQueue()
+ ).first;
+
+ if (iQueue != mpRecentlyUsedPageCaches->end())
+ {
+ iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache));
+ // Shorten the list of recently used caches to the allowed maximal length.
+ while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
+ iQueue->second.pop_back();
+ }
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx
new file mode 100644
index 000000000..077c48709
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsQueueProcessor.hxx"
+#include "SlsRequestQueue.hxx"
+#include "SlsBitmapCache.hxx"
+
+#include <sdpage.hxx>
+#include <comphelper/profilezone.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace sd::slidesorter::cache {
+
+//===== QueueProcessor ======================================================
+
+QueueProcessor::QueueProcessor (
+ RequestQueue& rQueue,
+ const std::shared_ptr<BitmapCache>& rpCache,
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling,
+ const SharedCacheContext& rpCacheContext)
+ : maTimer("sd::QueueProcessor maTimer"),
+ maPreviewSize(rPreviewSize),
+ mbDoSuperSampling(bDoSuperSampling),
+ mpCacheContext(rpCacheContext),
+ mrQueue(rQueue),
+ mpCache(rpCache),
+ mbIsPaused(false)
+{
+ maTimer.SetInvokeHandler (LINK(this,QueueProcessor,ProcessRequestHdl));
+ maTimer.SetTimeout (10);
+}
+
+QueueProcessor::~QueueProcessor()
+{
+}
+
+void QueueProcessor::Start (int nPriorityClass)
+{
+ if (mbIsPaused)
+ return;
+ if ( ! maTimer.IsActive())
+ {
+ if (nPriorityClass == 0)
+ maTimer.SetTimeout (10);
+ else
+ maTimer.SetTimeout (100);
+ maTimer.Start();
+ }
+}
+
+void QueueProcessor::Stop()
+{
+ if (maTimer.IsActive())
+ maTimer.Stop();
+}
+
+void QueueProcessor::Pause()
+{
+ mbIsPaused = true;
+}
+
+void QueueProcessor::Resume()
+{
+ mbIsPaused = false;
+ if ( ! mrQueue.IsEmpty())
+ Start(mrQueue.GetFrontPriorityClass());
+}
+
+void QueueProcessor::SetPreviewSize (
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling)
+{
+ maPreviewSize = rPreviewSize;
+ mbDoSuperSampling = bDoSuperSampling;
+}
+
+IMPL_LINK_NOARG(QueueProcessor, ProcessRequestHdl, Timer *, void)
+{
+ ProcessRequests();
+}
+
+void QueueProcessor::ProcessRequests()
+{
+ assert(mpCacheContext);
+
+ // Never process more than one request at a time in order to prevent the
+ // lock up of the edit view.
+ if ( ! mrQueue.IsEmpty()
+ && ! mbIsPaused
+ && mpCacheContext->IsIdle())
+ {
+ CacheKey aKey = nullptr;
+ RequestPriorityClass ePriorityClass (NOT_VISIBLE);
+ {
+ ::osl::MutexGuard aGuard (mrQueue.GetMutex());
+
+ if ( ! mrQueue.IsEmpty())
+ {
+ // Get the request with the highest priority from the queue.
+ ePriorityClass = mrQueue.GetFrontPriorityClass();
+ aKey = mrQueue.GetFront();
+ mrQueue.PopFront();
+ }
+ }
+
+ if (aKey != nullptr)
+ ProcessOneRequest(aKey, ePriorityClass);
+ }
+
+ // Schedule the processing of the next element(s).
+ {
+ ::osl::MutexGuard aGuard (mrQueue.GetMutex());
+ if ( ! mrQueue.IsEmpty())
+ Start(mrQueue.GetFrontPriorityClass());
+ else
+ {
+ comphelper::ProfileZone aZone("QueueProcessor finished processing all elements");
+ }
+ }
+}
+
+void QueueProcessor::ProcessOneRequest (
+ CacheKey aKey,
+ const RequestPriorityClass ePriorityClass)
+{
+ try
+ {
+ std::scoped_lock aGuard (maMutex);
+
+ // Create a new preview bitmap and store it in the cache.
+ if (mpCache != nullptr && mpCacheContext)
+ {
+ const SdPage* pSdPage = dynamic_cast<const SdPage*>(mpCacheContext->GetPage(aKey));
+ if (pSdPage != nullptr)
+ {
+ const BitmapEx aPreview (
+ maBitmapFactory.CreateBitmap(*pSdPage, maPreviewSize, mbDoSuperSampling));
+ mpCache->SetBitmap (pSdPage, aPreview, ePriorityClass!=NOT_VISIBLE);
+
+ // Initiate a repaint of the new preview.
+ mpCacheContext->NotifyPreviewCreation(aKey);
+ }
+ }
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "sd", "QueueProcessor");
+ }
+}
+
+void QueueProcessor::SetBitmapCache (
+ const std::shared_ptr<BitmapCache>& rpCache)
+{
+ mpCache = rpCache;
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx
new file mode 100644
index 000000000..0035bcbce
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <cache/SlsCacheContext.hxx>
+#include "SlsRequestPriorityClass.hxx"
+#include "SlsBitmapFactory.hxx"
+
+#include <vcl/timer.hxx>
+#include <mutex>
+
+namespace sd::slidesorter::cache {
+
+class BitmapCache;
+class RequestQueue;
+
+/** This queue processor is timer based, i.e. when an entry is added to the
+ queue and the processor is started with Start() in the base class a
+ timer is started that eventually calls ProcessRequest(). This is
+ repeated until the queue is empty or Stop() is called.
+*/
+class QueueProcessor final
+{
+public:
+ QueueProcessor (
+ RequestQueue& rQueue,
+ const std::shared_ptr<BitmapCache>& rpCache,
+ const Size& rPreviewSize,
+ const bool bDoSuperSampling,
+ const SharedCacheContext& rpCacheContext);
+ ~QueueProcessor();
+
+ /** Start the processor. This implementation is timer based and waits
+ a defined amount of time that depends on the given argument before
+ the next entry in the queue is processed.
+ @param nPriorityClass
+ A priority class of 0 tells the processor that a high priority
+ request is waiting in the queue. The time to wait is thus
+ shorter then that for a low priority request (denoted by a value
+ of 1.) When the timer is already running it is not modified.
+ */
+ void Start (int nPriorityClass);
+ void Stop();
+ void Pause();
+ void Resume();
+
+ void SetPreviewSize (
+ const Size& rSize,
+ const bool bDoSuperSampling);
+
+ /** Use this method when the page cache is (maybe) using a different
+ BitmapCache. This is usually necessary after calling
+ PageCacheManager::ChangeSize().
+ */
+ void SetBitmapCache (const std::shared_ptr<BitmapCache>& rpCache);
+
+private:
+ /** This mutex is used to guard the queue processor. Be careful not to
+ mix its use with that of the solar mutex.
+ */
+ std::mutex maMutex;
+
+ Timer maTimer;
+ DECL_LINK(ProcessRequestHdl, Timer *, void);
+ Size maPreviewSize;
+ bool mbDoSuperSampling;
+ SharedCacheContext mpCacheContext;
+ RequestQueue& mrQueue;
+ std::shared_ptr<BitmapCache> mpCache;
+ BitmapFactory maBitmapFactory;
+ bool mbIsPaused;
+
+ void ProcessRequests();
+ void ProcessOneRequest (
+ CacheKey aKey,
+ const RequestPriorityClass ePriorityClass);
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx b/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx
new file mode 100644
index 000000000..6fc6cabc9
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsRequestFactory.hxx"
+#include "SlsRequestQueue.hxx"
+
+namespace sd::slidesorter::cache {
+
+void RequestFactory::operator()(
+ RequestQueue& rRequestQueue,
+ const SharedCacheContext& rpCacheContext)
+{
+ std::shared_ptr<std::vector<CacheKey> > aKeys;
+
+ // Add the requests for the visible pages.
+ aKeys = rpCacheContext->GetEntryList(true);
+ if (aKeys != nullptr)
+ {
+ for (const auto& rKey : *aKeys)
+ rRequestQueue.AddRequest(rKey, VISIBLE_NO_PREVIEW);
+ }
+
+ // Add the requests for the non-visible pages.
+ aKeys = rpCacheContext->GetEntryList(false);
+ if (aKeys != nullptr)
+ {
+ for (const auto& rKey : *aKeys)
+ rRequestQueue.AddRequest(rKey, NOT_VISIBLE);
+ }
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx b/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx
new file mode 100644
index 000000000..3f4207725
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <cache/SlsCacheContext.hxx>
+
+namespace sd::slidesorter::cache
+{
+class RequestQueue;
+
+class RequestFactory
+{
+public:
+ void operator()(RequestQueue& rRequestQueue, const SharedCacheContext& rpCacheContext);
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx b/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx
new file mode 100644
index 000000000..2c84ecbcf
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+namespace sd::slidesorter::cache
+{
+/** Each request for a preview creation has a priority. This enum defines
+ the available priorities. The special values MIN_CLASS and MAX_CLASS
+ are/can be used for validation and have to be kept up-to-date.
+*/
+enum RequestPriorityClass
+{
+ MIN_CLASS = 0,
+
+ // The slide is visible. A preview does not yet exist.
+ VISIBLE_NO_PREVIEW = MIN_CLASS,
+ // The slide is visible. A preview exists but is not up-to-date anymore.
+ VISIBLE_OUTDATED_PREVIEW,
+ // The slide is not visible.
+ NOT_VISIBLE,
+
+ MAX_CLASS = NOT_VISIBLE
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx b/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx
new file mode 100644
index 000000000..931c1a8f6
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "SlsRequestQueue.hxx"
+
+#include <sal/log.hxx>
+
+#include <svx/svdpage.hxx>
+
+#include <set>
+
+namespace sd::slidesorter::cache {
+
+namespace {
+
+/** This class extends the actual request data with additional information
+ that is used by the priority queues.
+*/
+class Request
+{
+public:
+ Request (
+ CacheKey aKey, sal_Int32 nPriority, RequestPriorityClass eClass)
+ : maKey(aKey), mnPriorityInClass(nPriority), meClass(eClass)
+ {}
+ /** Sort requests according to priority classes and then to priorities.
+ */
+ class Comparator { public:
+ bool operator() (const Request& rRequest1, const Request& rRequest2)
+ const
+ {
+ if (rRequest1.meClass == rRequest2.meClass)
+ {
+ if (rRequest1.mnPriorityInClass == rRequest2.mnPriorityInClass)
+ {
+ return rRequest1.maKey < rRequest2.maKey;
+ }
+ return rRequest1.mnPriorityInClass > rRequest2.mnPriorityInClass;
+ }
+ return rRequest1.meClass < rRequest2.meClass;
+ }
+ };
+ /** Request data is compared arbitrarily by their addresses in memory.
+ This just establishes an order so that the STL containers are happy.
+ The order is not semantically interpreted.
+ */
+ class DataComparator
+ {
+ public:
+ explicit DataComparator (const CacheKey aKey)
+ : maKey(aKey)
+ {
+ }
+ bool operator() (const Request& rRequest) const
+ {
+ return maKey == rRequest.maKey;
+ }
+ private:
+ const CacheKey maKey;
+ };
+
+ CacheKey maKey;
+ sal_Int32 mnPriorityInClass;
+ RequestPriorityClass meClass;
+};
+
+}
+
+class RequestQueue::Container
+ : public ::std::set<
+ Request,
+ Request::Comparator>
+{
+};
+
+//===== GenericRequestQueue =================================================
+
+RequestQueue::RequestQueue (const SharedCacheContext& rpCacheContext)
+ : mpRequestQueue(new Container),
+ mpCacheContext(rpCacheContext),
+ mnMinimumPriority(0),
+ mnMaximumPriority(1)
+{
+}
+
+RequestQueue::~RequestQueue()
+{
+ Clear();
+}
+
+void RequestQueue::AddRequest (
+ CacheKey aKey,
+ RequestPriorityClass eRequestClass)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ assert(eRequestClass>=MIN_CLASS && eRequestClass<=MAX_CLASS);
+
+ // If the request is already a member of the queue then remove it so
+ // that the following insertion will use the new prioritization.
+#if OSL_DEBUG_LEVEL >=2
+ bool bRemoved =
+#endif
+ RemoveRequest(aKey);
+
+ // The priority of the request inside its priority class is defined by
+ // the page number. This ensures a strict top-to-bottom, left-to-right
+ // order.
+ sal_Int32 nPriority (mpCacheContext->GetPriority(aKey));
+ Request aRequest (aKey, nPriority, eRequestClass);
+
+ std::pair<Container::iterator,bool> ret = mpRequestQueue->insert(aRequest);
+ bool bInserted = ret.second;
+
+ if (bInserted)
+ {
+ SdrPage *pPage = const_cast<SdrPage*>(aRequest.maKey);
+ pPage->AddPageUser(*this);
+ }
+
+#if OSL_DEBUG_LEVEL >=2
+ SAL_INFO("sd.sls", __func__ << ": " << (bRemoved?"replaced":"added")
+ << " request for page " << ((aKey->GetPageNum()-1)/2)
+ << " with priority class " << static_cast<int>(eRequestClass));
+#endif
+}
+
+void RequestQueue::PageInDestruction(const SdrPage& rPage)
+{
+ //remove any requests pending for this page which is going away now
+ RemoveRequest(&rPage);
+}
+
+#if OSL_DEBUG_LEVEL >=2
+bool
+#else
+void
+#endif
+RequestQueue::RemoveRequest(
+ CacheKey aKey)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+#if OSL_DEBUG_LEVEL >=2
+ bool bIsRemoved = false;
+#endif
+ while(true)
+ {
+ Container::const_iterator aRequestIterator = ::std::find_if (
+ mpRequestQueue->begin(),
+ mpRequestQueue->end(),
+ Request::DataComparator(aKey));
+ if (aRequestIterator != mpRequestQueue->end())
+ {
+ if (aRequestIterator->mnPriorityInClass == mnMinimumPriority+1)
+ mnMinimumPriority++;
+ else if (aRequestIterator->mnPriorityInClass == mnMaximumPriority-1)
+ mnMaximumPriority--;
+
+ SdrPage *pPage = const_cast<SdrPage*>(aRequestIterator->maKey);
+ pPage->RemovePageUser(*this);
+ mpRequestQueue->erase(aRequestIterator);
+#if OSL_DEBUG_LEVEL >=2
+ bIsRemoved = true;
+#endif
+ }
+ else
+ break;
+ }
+#if OSL_DEBUG_LEVEL >=2
+ return bIsRemoved;
+#endif
+
+}
+
+void RequestQueue::ChangeClass (
+ CacheKey aKey,
+ RequestPriorityClass eNewRequestClass)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ assert(eNewRequestClass>=MIN_CLASS && eNewRequestClass<=MAX_CLASS);
+
+ Container::const_iterator iRequest (
+ ::std::find_if (
+ mpRequestQueue->begin(),
+ mpRequestQueue->end(),
+ Request::DataComparator(aKey)));
+ if (iRequest!=mpRequestQueue->end() && iRequest->meClass!=eNewRequestClass)
+ {
+ AddRequest(aKey, eNewRequestClass);
+ }
+}
+
+CacheKey RequestQueue::GetFront()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ if (mpRequestQueue->empty())
+ throw css::uno::RuntimeException("RequestQueue::GetFront(): queue is empty",
+ nullptr);
+
+ return mpRequestQueue->begin()->maKey;
+}
+
+RequestPriorityClass RequestQueue::GetFrontPriorityClass()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ if (mpRequestQueue->empty())
+ throw css::uno::RuntimeException("RequestQueue::GetFrontPriorityClass(): queue is empty",
+ nullptr);
+
+ return mpRequestQueue->begin()->meClass;
+}
+
+void RequestQueue::PopFront()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ if ( mpRequestQueue->empty())
+ return;
+
+ Container::const_iterator aIter(mpRequestQueue->begin());
+ SdrPage *pPage = const_cast<SdrPage*>(aIter->maKey);
+ pPage->RemovePageUser(*this);
+ mpRequestQueue->erase(aIter);
+
+ // Reset the priority counter if possible.
+ if (mpRequestQueue->empty())
+ {
+ mnMinimumPriority = 0;
+ mnMaximumPriority = 1;
+ }
+}
+
+bool RequestQueue::IsEmpty()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+ return mpRequestQueue->empty();
+}
+
+void RequestQueue::Clear()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ for (const auto& rItem : *mpRequestQueue)
+ {
+ SdrPage *pPage = const_cast<SdrPage*>(rItem.maKey);
+ pPage->RemovePageUser(*this);
+ }
+
+ mpRequestQueue->clear();
+ mnMinimumPriority = 0;
+ mnMaximumPriority = 1;
+}
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx b/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx
new file mode 100644
index 000000000..618ba5801
--- /dev/null
+++ b/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "SlsRequestPriorityClass.hxx"
+#include <cache/SlsCacheContext.hxx>
+#include <osl/mutex.hxx>
+#include <svx/sdrpageuser.hxx>
+
+#include <memory>
+
+namespace sd::slidesorter::cache
+{
+/** The request queue stores requests that are described by the Request
+ sorted according to priority class and then priority.
+*/
+class RequestQueue : public sdr::PageUser
+{
+public:
+ explicit RequestQueue(const SharedCacheContext& rpCacheContext);
+ virtual ~RequestQueue();
+
+ /** Insert a request with highest or lowest priority in its priority
+ class. When the request is already present then it is first
+ removed. This effect is then a re-prioritization.
+ @param aKey
+ The request.
+ @param eRequestClass
+ The priority class in which to insert the request with highest
+ or lowest priority.
+ @param bInsertWithHighestPriority
+ When this flag is <TRUE/> the request is inserted with highest
+ priority in its class. When <FALSE/> the request is inserted
+ with lowest priority.
+ */
+ void AddRequest(CacheKey aKey, RequestPriorityClass eRequestClass);
+
+ /** Remove the specified request from the queue.
+ @param aKey
+ It is OK when the specified request is not a member of the
+ queue.
+ */
+#if OSL_DEBUG_LEVEL >= 2
+ bool
+#else
+ void
+#endif
+ RemoveRequest(CacheKey aKey);
+
+ /** Change the priority class of the specified request.
+ */
+ void ChangeClass(CacheKey aKey, RequestPriorityClass eNewRequestClass);
+
+ /** Get the request with the highest priority int the highest priority class.
+ */
+ CacheKey GetFront();
+
+ // For debugging.
+ RequestPriorityClass GetFrontPriorityClass();
+
+ /** Really a synonym for RemoveRequest(GetFront());
+ */
+ void PopFront();
+
+ /** Returns <TRUE/> when there is no element in the queue.
+ */
+ bool IsEmpty();
+
+ /** Remove all requests from the queue. This resets the minimum and
+ maximum priorities to their default values.
+ */
+ void Clear();
+
+ /** Return the mutex that guards the access to the priority queue.
+ */
+ ::osl::Mutex& GetMutex() { return maMutex; }
+
+ /** Ensure we don't hand out a page deleted before anyone got a
+ chance to process it
+ */
+ virtual void PageInDestruction(const SdrPage& rPage) override;
+
+private:
+ ::osl::Mutex maMutex;
+ class Container;
+ std::unique_ptr<Container> mpRequestQueue;
+ SharedCacheContext mpCacheContext;
+
+ /** A lower bound of the lowest priority of all elements in the queues.
+ The start value is 0. It is assigned and then decreased every time
+ when an element is inserted or marked as the request with lowest
+ priority.
+ */
+ int mnMinimumPriority;
+ /** An upper bound of the highest priority of all elements in the queues.
+ The start value is 1. It is assigned and then increased every time
+ when an element is inserted or marked as the request with highest
+ priority.
+ */
+ int mnMaximumPriority;
+};
+
+} // end of namespace ::sd::slidesorter::cache
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */