vcl/CppunitTest_vcl_graphic_test.mk  |    1 
 vcl/inc/graphic/Manager.hxx          |   67 ++-----
 vcl/inc/graphic/MemoryManaged.hxx    |  145 ++++++++++++++++
 vcl/inc/impgraph.hxx                 |   12 +
 vcl/qa/cppunit/GraphicMemoryTest.cxx |  310 +++++++++++++++++++++++++++++++++++
 vcl/source/app/svapp.cxx             |    4 
 vcl/source/gdi/graph.cxx             |   30 +--
 vcl/source/gdi/impgraph.cxx          |   93 +++++++---
 vcl/source/graphic/Manager.cxx       |  286 +++++++++-----------------------
 9 files changed, 658 insertions(+), 290 deletions(-)

New commits:
commit 324f2e135427f2f24cf7eb9a4fab4aa903329ae5
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Mon Mar 18 00:17:54 2024 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Sat Mar 30 15:21:10 2024 +0100

    vcl: change (graphic) Manager into a general memory manager
    
    Graphic memory manager was changes so that it can work with any
    object that implements a specific interface (MemoryManaged). With
    this it will be possible to use other objects (that take a lot of
    memory) to be managed by the manager. It is also a first step to
    move memory managin responsibilities away from Graphic and move
    it into the specific objects instead (BitmapEx, Animation and
    VectorGraphic).
    
    Change-Id: I7638bd89a1c9ece5c4bc95b506d2192492894ef3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164958
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/vcl/CppunitTest_vcl_graphic_test.mk 
b/vcl/CppunitTest_vcl_graphic_test.mk
index 6089512f5b5b..33e818e23f05 100644
--- a/vcl/CppunitTest_vcl_graphic_test.mk
+++ b/vcl/CppunitTest_vcl_graphic_test.mk
@@ -11,6 +11,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,vcl_graphic_test))
 
 $(eval $(call gb_CppunitTest_add_exception_objects,vcl_graphic_test, \
     vcl/qa/cppunit/GraphicTest \
+    vcl/qa/cppunit/GraphicMemoryTest \
     vcl/qa/cppunit/GraphicDescriptorTest \
     vcl/qa/cppunit/GraphicFormatDetectorTest \
     vcl/qa/cppunit/GraphicNativeMetadataTest \
diff --git a/vcl/inc/graphic/Manager.hxx b/vcl/inc/graphic/Manager.hxx
index d239f6a8b01d..0a4ee3fbc84d 100644
--- a/vcl/inc/graphic/Manager.hxx
+++ b/vcl/inc/graphic/Manager.hxx
@@ -11,66 +11,51 @@
 
 #include <sal/types.h>
 #include <rtl/strbuf.hxx>
-#include <vcl/bitmapex.hxx>
-#include <vcl/animate/Animation.hxx>
-#include <vcl/vectorgraphicdata.hxx>
 #include <vcl/timer.hxx>
-#include <vcl/GraphicExternalLink.hxx>
-#include <vcl/gfxlink.hxx>
 
 #include <memory>
 #include <mutex>
 #include <chrono>
 #include <o3tl/sorted_vector.hxx>
 
-class ImpGraphic;
-
 namespace vcl::graphic
 {
-class Manager final
+class MemoryManaged;
+
+class VCL_DLLPUBLIC MemoryManager final
 {
 private:
+    o3tl::sorted_vector<MemoryManaged*> maObjectList;
+    sal_Int64 mnTotalSize = 0;
     std::mutex maMutex; // instead of SolarMutex because graphics can live 
past vcl main
-    o3tl::sorted_vector<ImpGraphic*> m_pImpGraphicList;
-    std::chrono::seconds mnAllowedIdleTime;
-    bool mbSwapEnabled;
-    bool mbReducingGraphicMemory;
-    sal_Int64 mnMemoryLimit;
-    sal_Int64 mnUsedSize;
-    Timer maSwapOutTimer;
-
-    Manager();
-
-    void registerGraphic(const std::shared_ptr<ImpGraphic>& rImpGraphic);
-    void loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll);
 
-    DECL_LINK(SwapOutTimerHandler, Timer*, void);
+    std::chrono::seconds mnAllowedIdleTime = std::chrono::seconds(1);
+    bool mbSwapEnabled = true;
+    bool mbReducingGraphicMemory = false;
+    sal_Int64 mnMemoryLimit = 10'000'000;
+    Timer maSwapOutTimer;
+    sal_Int32 mnTimeout = 1'000;
+    sal_Int64 mnSmallFrySize = 100'000;
 
-    static sal_Int64 getGraphicSizeBytes(const ImpGraphic* pImpGraphic);
-    void reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll = false);
+    DECL_LINK(ReduceMemoryTimerHandler, Timer*, void);
 
 public:
-    static Manager& get();
-
-    void dropCache();
-    void dumpState(rtl::OStringBuffer& rState);
+    MemoryManager();
+    void registerObject(MemoryManaged* pObject);
+    void unregisterObject(MemoryManaged* pObject);
+    void changeExisting(MemoryManaged* pObject, sal_Int64 nNewSize);
 
-    void swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes);
-    void swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes);
+    void swappedIn(MemoryManaged* pObject, sal_Int64 nNewSize);
+    void swappedOut(MemoryManaged* pObject, sal_Int64 nNewSize);
 
-    void changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 nOldSize);
-    void unregisterGraphic(ImpGraphic* pImpGraphic);
+    static MemoryManager& get();
+    o3tl::sorted_vector<MemoryManaged*> const& getManagedObjects() { return 
maObjectList; }
+    sal_Int64 getTotalSize() { return mnTotalSize; }
 
-    std::shared_ptr<ImpGraphic> copy(std::shared_ptr<ImpGraphic> const& 
pImpGraphic);
-    std::shared_ptr<ImpGraphic> newInstance();
-    std::shared_ptr<ImpGraphic> newInstance(const BitmapEx& rBitmapEx);
-    std::shared_ptr<ImpGraphic> newInstance(std::shared_ptr<GfxLink> const& 
rLink,
-                                            sal_Int32 nPageIndex = 0);
-    std::shared_ptr<ImpGraphic>
-    newInstance(const std::shared_ptr<VectorGraphicData>& 
rVectorGraphicDataPtr);
-    std::shared_ptr<ImpGraphic> newInstance(const Animation& rAnimation);
-    std::shared_ptr<ImpGraphic> newInstance(const GDIMetaFile& rMtf);
-    std::shared_ptr<ImpGraphic> newInstance(const GraphicExternalLink& 
rGraphicLink);
+    void reduceMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll = 
false);
+    void loopAndReduceMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll = false);
+    void reduceAllAndNow();
+    void dumpState(rtl::OStringBuffer& rState);
 };
 
 } // end namespace vcl::graphic
diff --git a/vcl/inc/graphic/MemoryManaged.hxx 
b/vcl/inc/graphic/MemoryManaged.hxx
new file mode 100644
index 000000000000..d88e87979ba9
--- /dev/null
+++ b/vcl/inc/graphic/MemoryManaged.hxx
@@ -0,0 +1,145 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/strbuf.hxx>
+#include <vcl/timer.hxx>
+
+#include <memory>
+#include <mutex>
+#include <chrono>
+#include <o3tl/sorted_vector.hxx>
+
+namespace vcl::graphic
+{
+class VCL_DLLPUBLIC MemoryManaged
+{
+private:
+    sal_Int64 mnCurrentSizeBytes = 0;
+    bool mbIsRegistered = false;
+
+protected:
+    // Copy
+    MemoryManaged(MemoryManaged const& rMemoryManaged)
+        : mbIsRegistered(rMemoryManaged.mbIsRegistered)
+    {
+        // if the original is registered we need to register as well
+        if (mbIsRegistered)
+            registerIntoManager();
+    }
+
+    // Move
+    MemoryManaged(MemoryManaged&& rMemoryManaged)
+        : mbIsRegistered(rMemoryManaged.mbIsRegistered)
+    {
+        // if the original is registered we need to register as well
+        if (mbIsRegistered)
+            registerIntoManager();
+    }
+
+    MemoryManaged(bool bRegister)
+    {
+        if (bRegister)
+            registerIntoManager();
+    }
+
+    virtual ~MemoryManaged() { unregisterFromManager(); }
+
+    void updateCurrentSizeInBytes(sal_Int64 nNewSize)
+    {
+        if (mbIsRegistered)
+        {
+            auto& rManager = vcl::graphic::MemoryManager::get();
+            rManager.changeExisting(this, nNewSize);
+        }
+        else
+        {
+            mnCurrentSizeBytes = nNewSize;
+        }
+    }
+
+    bool isRegistered() const { return mbIsRegistered; }
+
+    void changeExisting(sal_Int64 nNewSize)
+    {
+        if (mbIsRegistered)
+        {
+            auto& rManager = vcl::graphic::MemoryManager::get();
+            rManager.changeExisting(this, nNewSize);
+        }
+        else
+        {
+            mnCurrentSizeBytes = nNewSize;
+        }
+    }
+
+    void swappedIn(sal_Int64 nNewSize)
+    {
+        if (mbIsRegistered)
+        {
+            auto& rManager = vcl::graphic::MemoryManager::get();
+            rManager.swappedIn(this, nNewSize);
+        }
+        else
+        {
+            mnCurrentSizeBytes = nNewSize;
+        }
+    }
+
+    void swappedOut(sal_Int64 nNewSize)
+    {
+        if (mbIsRegistered)
+        {
+            auto& rManager = vcl::graphic::MemoryManager::get();
+            rManager.swappedOut(this, nNewSize);
+        }
+        else
+        {
+            mnCurrentSizeBytes = nNewSize;
+        }
+    }
+
+public:
+    sal_Int64 getCurrentSizeInBytes() const { return mnCurrentSizeBytes; }
+
+    void setCurrentSizeInBytes(sal_Int64 nSize) { mnCurrentSizeBytes = nSize; }
+
+protected:
+    void registerIntoManager()
+    {
+        if (!mbIsRegistered)
+        {
+            auto& rManager = vcl::graphic::MemoryManager::get();
+            rManager.registerObject(this);
+            mbIsRegistered = true;
+        }
+    }
+
+    void unregisterFromManager()
+    {
+        if (mbIsRegistered)
+        {
+            auto& rManager = vcl::graphic::MemoryManager::get();
+            rManager.unregisterObject(this);
+            mbIsRegistered = false;
+        }
+    }
+
+public:
+    virtual bool canReduceMemory() const = 0;
+    virtual bool reduceMemory() = 0;
+    virtual std::chrono::high_resolution_clock::time_point getLastUsed() const 
= 0;
+    virtual void dumpState(rtl::OStringBuffer& rState) = 0;
+};
+
+} // end namespace vcl::graphic
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/impgraph.hxx b/vcl/inc/impgraph.hxx
index 707810b9de2e..35542db74641 100644
--- a/vcl/inc/impgraph.hxx
+++ b/vcl/inc/impgraph.hxx
@@ -24,6 +24,7 @@
 #include <vcl/gdimtf.hxx>
 #include <vcl/graph.hxx>
 #include "graphic/Manager.hxx"
+#include "graphic/MemoryManaged.hxx"
 #include "graphic/GraphicID.hxx"
 #include <optional>
 
@@ -56,11 +57,10 @@ enum class GraphicContentType : sal_Int32
     Vector
 };
 
-class ImpGraphic final
+class ImpGraphic final  : public vcl::graphic::MemoryManaged
 {
     friend class Graphic;
     friend class GraphicID;
-    friend class vcl::graphic::Manager;
 
 private:
 
@@ -160,6 +160,8 @@ private:
 
     sal_uLong           getSizeBytes() const;
 
+    void ensureCurrentSizeInBytes();
+
     void                draw(OutputDevice& rOutDev, const Point& rDestPt) 
const;
     void                draw(OutputDevice& rOutDev, const Point& rDestPt,
                              const Size& rDestSize) const;
@@ -212,6 +214,10 @@ private:
     // Set the pref map mode, but don't force swap-in
     void setValuesForPrefMapMod(const MapMode& rPrefMapMode);
 
+    bool canReduceMemory() const override;
+    bool reduceMemory() override;
+    std::chrono::high_resolution_clock::time_point getLastUsed() const 
override;
+
 public:
     void resetChecksum() { mnChecksum = 0; }
     bool swapIn();
@@ -220,7 +226,7 @@ public:
     VCL_DLLPUBLIC SvStream* getSwapFileStream() const;
     // public only because of use in GraphicFilter
     void updateFromLoadedGraphic(const ImpGraphic* graphic);
-    void dumpState(rtl::OStringBuffer &rState);
+    void dumpState(rtl::OStringBuffer &rState) override;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/GraphicMemoryTest.cxx 
b/vcl/qa/cppunit/GraphicMemoryTest.cxx
new file mode 100644
index 000000000000..95e64e471246
--- /dev/null
+++ b/vcl/qa/cppunit/GraphicMemoryTest.cxx
@@ -0,0 +1,310 @@
+/* -*- 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/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <tools/stream.hxx>
+
+#include <impgraph.hxx>
+#include <graphic/GraphicFormatDetector.hxx>
+#include <graphic/MemoryManaged.hxx>
+
+using namespace css;
+
+namespace
+{
+BitmapEx createBitmap(Size aSize, bool bAlpha = false)
+{
+    Bitmap aBitmap(aSize, vcl::PixelFormat::N24_BPP);
+    aBitmap.Erase(COL_LIGHTRED);
+
+    aBitmap.SetPrefSize(Size(aSize.Width() * 2, aSize.Height() * 3));
+    aBitmap.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+
+    if (bAlpha)
+    {
+        sal_uInt8 uAlphaValue = 0x80;
+        AlphaMask aAlphaMask(aSize, &uAlphaValue);
+
+        return BitmapEx(aBitmap, aAlphaMask);
+    }
+    else
+    {
+        return BitmapEx(aBitmap);
+    }
+}
+
+void createBitmapAndExportForType(SvStream& rStream, std::u16string_view sType,
+                                  Size aSize = Size(120, 100), bool bAlpha = 
false)
+{
+    BitmapEx aBitmapEx = createBitmap(aSize, bAlpha);
+
+    uno::Sequence<beans::PropertyValue> aFilterData;
+    GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+    sal_uInt16 nFilterFormat = 
rGraphicFilter.GetExportFormatNumberForShortName(sType);
+    rGraphicFilter.ExportGraphic(aBitmapEx, u"none", rStream, nFilterFormat, 
&aFilterData);
+
+    rStream.Seek(STREAM_SEEK_TO_BEGIN);
+}
+
+Graphic makeUnloadedGraphic(std::u16string_view sType, Size aSize = Size(120, 
100),
+                            bool bAlpha = false)
+{
+    SvMemoryStream aStream;
+    GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+    createBitmapAndExportForType(aStream, sType, aSize, bAlpha);
+    return rGraphicFilter.ImportUnloadedGraphic(aStream);
+}
+
+constexpr OUString DATA_DIRECTORY = u"/vcl/qa/cppunit/data/"_ustr;
+
+Graphic loadGraphic(std::u16string_view rFilename)
+{
+    test::Directories aDirectories;
+    OUString aFilename = aDirectories.getURLFromSrc(DATA_DIRECTORY) + 
rFilename;
+    SvFileStream aFileStream(aFilename, StreamMode::READ);
+    GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+
+    Graphic aGraphic;
+    CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, rGraphicFilter.ImportGraphic(aGraphic, 
u"", aFileStream,
+                                                                    
GRFILTER_FORMAT_DONTKNOW));
+    return aGraphic;
+}
+
+Graphic loadUnloadedGraphic(std::u16string_view rFilename)
+{
+    test::Directories aDirectories;
+    OUString aFilename = aDirectories.getURLFromSrc(DATA_DIRECTORY) + 
rFilename;
+    SvFileStream aFileStream(aFilename, StreamMode::READ);
+    GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+    return rGraphicFilter.ImportUnloadedGraphic(aFileStream);
+}
+
+} // end anonymous
+
+class GraphicMemoryTest : public test::BootstrapFixture
+{
+};
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Empty)
+{
+    auto& rManager = vcl::graphic::MemoryManager::get();
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic1;
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic2;
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic3;
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    aGraphic1 = aGraphic2;
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    aGraphic2 = aGraphic3;
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Unloaded)
+{
+    auto& rManager = vcl::graphic::MemoryManager::get();
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic_100 = makeUnloadedGraphic(u"jpg", Size(100, 100));
+    Graphic aGraphic_200 = makeUnloadedGraphic(u"png", Size(200, 100));
+    Graphic aGraphic_300 = makeUnloadedGraphic(u"jpg", Size(300, 100));
+
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    aGraphic_100.makeAvailable();
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(30000), rManager.getTotalSize());
+    CPPUNIT_ASSERT_EQUAL(sal_uLong(30000), aGraphic_100.GetSizeBytes());
+
+    aGraphic_200.makeAvailable();
+    CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(90000), rManager.getTotalSize());
+    CPPUNIT_ASSERT_EQUAL(sal_uLong(30000), aGraphic_100.GetSizeBytes());
+    CPPUNIT_ASSERT_EQUAL(sal_uLong(60000), aGraphic_200.GetSizeBytes());
+
+    aGraphic_300.makeAvailable();
+    CPPUNIT_ASSERT_EQUAL(size_t(3), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), rManager.getTotalSize());
+    CPPUNIT_ASSERT_EQUAL(sal_uLong(30000), aGraphic_100.GetSizeBytes());
+    CPPUNIT_ASSERT_EQUAL(sal_uLong(60000), aGraphic_200.GetSizeBytes());
+    CPPUNIT_ASSERT_EQUAL(sal_uLong(90000), aGraphic_300.GetSizeBytes());
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Destrucion)
+{
+    auto& rManager = vcl::graphic::MemoryManager::get();
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic_100 = makeUnloadedGraphic(u"jpg", Size(100, 100));
+    Graphic aGraphic_200 = makeUnloadedGraphic(u"png", Size(200, 100));
+    Graphic aGraphic_300 = makeUnloadedGraphic(u"jpg", Size(300, 100));
+
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    aGraphic_100.makeAvailable();
+    aGraphic_200.makeAvailable();
+    aGraphic_300.makeAvailable();
+    CPPUNIT_ASSERT_EQUAL(size_t(3), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), rManager.getTotalSize());
+
+    aGraphic_200 = Graphic();
+    CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(120000), rManager.getTotalSize());
+
+    aGraphic_300 = Graphic();
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(30000), rManager.getTotalSize());
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Copy)
+{
+    auto& rManager = vcl::graphic::MemoryManager::get();
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic_100 = makeUnloadedGraphic(u"jpg", Size(100, 100));
+    Graphic aGraphic_200 = makeUnloadedGraphic(u"png", Size(200, 100));
+    Graphic aGraphic_300 = makeUnloadedGraphic(u"jpg", Size(300, 100));
+
+    Graphic aSVG = loadUnloadedGraphic(u"SimpleExample.svg");
+
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    aGraphic_100.makeAvailable();
+    aGraphic_200.makeAvailable();
+    aGraphic_300.makeAvailable();
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager)
+{
+    auto& rManager = vcl::graphic::MemoryManager::get();
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphic;
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    Graphic aGraphicPng = loadGraphic(u"TypeDetectionExample.png");
+    {
+        CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphicPng.GetType());
+        CPPUNIT_ASSERT_EQUAL(sal_uLong(300), aGraphicPng.GetSizeBytes());
+        CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.isAvailable());
+    }
+
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(300), rManager.getTotalSize());
+
+    CPPUNIT_ASSERT_EQUAL(false, 
aGraphicPng.ImplGetImpGraphic()->isSwappedOut());
+    CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.ImplGetImpGraphic()->swapOut());
+    CPPUNIT_ASSERT_EQUAL(true, 
aGraphicPng.ImplGetImpGraphic()->isSwappedOut());
+    CPPUNIT_ASSERT_EQUAL(false, aGraphicPng.isAvailable());
+
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(0), rManager.getTotalSize());
+
+    Graphic aGraphicJpg = makeUnloadedGraphic(u"jpg");
+    {
+        CPPUNIT_ASSERT_EQUAL(tools::Long(120), 
aGraphicJpg.GetSizePixel().Width());
+        CPPUNIT_ASSERT_EQUAL(tools::Long(100), 
aGraphicJpg.GetSizePixel().Height());
+        CPPUNIT_ASSERT_EQUAL(false, aGraphicJpg.isAvailable());
+    }
+
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(0), rManager.getTotalSize());
+
+    aGraphicJpg.makeAvailable();
+
+    CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(36000), rManager.getTotalSize());
+
+    CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.makeAvailable());
+    CPPUNIT_ASSERT_EQUAL(false, 
aGraphicPng.ImplGetImpGraphic()->isSwappedOut());
+
+    CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(36300), rManager.getTotalSize());
+
+    aGraphicJpg = aGraphic;
+
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(300), rManager.getTotalSize());
+}
+
+namespace
+{
+class TestManaged : public vcl::graphic::MemoryManaged
+{
+public:
+    std::chrono::high_resolution_clock::time_point maLastUsed
+        = std::chrono::high_resolution_clock::now();
+
+    TestManaged(bool bRegister, sal_Int64 nSize = 0)
+        : MemoryManaged(bRegister)
+    {
+        updateCurrentSizeInBytes(nSize);
+    }
+
+    void setCurrentSize(sal_Int64 nSize) { updateCurrentSizeInBytes(nSize); }
+
+    void callRegister() { registerIntoManager(); }
+
+    void callUnregister() { unregisterFromManager(); }
+
+    bool canReduceMemory() const override { return false; }
+
+    bool reduceMemory() override { return false; }
+
+    std::chrono::high_resolution_clock::time_point getLastUsed() const override
+    {
+        return maLastUsed;
+    }
+
+    void dumpState(rtl::OStringBuffer& /*rState*/) override {}
+};
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManagerX)
+{
+    auto& rManager = vcl::graphic::MemoryManager::get();
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    TestManaged aTestManaged(false, 1000);
+    CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+    TestManaged aTestManaged2(true, 100);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(100), rManager.getTotalSize());
+
+    aTestManaged2.setCurrentSize(400);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(400), rManager.getTotalSize());
+
+    aTestManaged.setCurrentSize(600);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(400), rManager.getTotalSize());
+
+    aTestManaged.callRegister();
+    CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(1000), rManager.getTotalSize());
+
+    aTestManaged.callUnregister();
+    CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(400), rManager.getTotalSize());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index e0995e0793e2..3b150e1bd956 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -1775,7 +1775,7 @@ void dumpState(rtl::OStringBuffer &rState)
         pWin = Application::GetNextTopLevelWindow( pWin );
     }
 
-    vcl::graphic::Manager::get().dumpState(rState);
+    vcl::graphic::MemoryManager::get().dumpState(rState);
 
     pSVData->dumpState(rState);
 
@@ -1792,7 +1792,7 @@ void trimMemory(int nTarget)
         if (!pSVData) // shutting down
             return;
         pSVData->dropCaches();
-        vcl::graphic::Manager::get().dropCache();
+        vcl::graphic::MemoryManager::get().reduceAllAndNow();
         // TODO: ideally - free up any deeper dirtied thread stacks.
         // comphelper::ThreadPool::getSharedOptimalPool().shutdown();
     }
diff --git a/vcl/source/gdi/graph.cxx b/vcl/source/gdi/graph.cxx
index 252ee189b892..2c3e05235225 100644
--- a/vcl/source/gdi/graph.cxx
+++ b/vcl/source/gdi/graph.cxx
@@ -159,14 +159,14 @@ void ImplDrawDefault(OutputDevice& rOutDev, const 
OUString* pText,
 } // end anonymous namespace
 
 Graphic::Graphic()
-    : mxImpGraphic(vcl::graphic::Manager::get().newInstance())
+    : mxImpGraphic(new ImpGraphic())
 {
 }
 
 Graphic::Graphic(const Graphic& rGraphic)
 {
     if( rGraphic.IsAnimated() )
-        mxImpGraphic = 
vcl::graphic::Manager::get().copy(rGraphic.mxImpGraphic);
+        mxImpGraphic = std::make_shared<ImpGraphic>(*rGraphic.mxImpGraphic);
     else
         mxImpGraphic = rGraphic.mxImpGraphic;
 }
@@ -177,17 +177,17 @@ Graphic::Graphic(Graphic&& rGraphic) noexcept
 }
 
 Graphic::Graphic(std::shared_ptr<GfxLink> const & rGfxLink, sal_Int32 
nPageIndex)
-    : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rGfxLink, 
nPageIndex))
+    : mxImpGraphic(new ImpGraphic(rGfxLink, nPageIndex))
 {
 }
 
 Graphic::Graphic(GraphicExternalLink const & rGraphicExternalLink)
-    : 
mxImpGraphic(vcl::graphic::Manager::get().newInstance(rGraphicExternalLink))
+    : mxImpGraphic(new ImpGraphic(rGraphicExternalLink))
 {
 }
 
-Graphic::Graphic(const BitmapEx& rBmpEx)
-    : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rBmpEx))
+Graphic::Graphic(const BitmapEx& rBitmapEx)
+    : mxImpGraphic(new ImpGraphic(rBitmapEx))
 {
 }
 
@@ -195,7 +195,7 @@ Graphic::Graphic(const BitmapEx& rBmpEx)
 // and we need to be able to see and preserve 'stock' images too.
 Graphic::Graphic(const Image& rImage)
     // FIXME: should really defer the BitmapEx load.
-    : mxImpGraphic(std::make_shared<ImpGraphic>(rImage.GetBitmapEx()))
+    : mxImpGraphic(new ImpGraphic(rImage.GetBitmapEx()))
 {
     OUString aStock = rImage.GetStock();
     if (aStock.getLength())
@@ -203,17 +203,17 @@ Graphic::Graphic(const Image& rImage)
 }
 
 Graphic::Graphic(const std::shared_ptr<VectorGraphicData>& 
rVectorGraphicDataPtr)
-    : 
mxImpGraphic(vcl::graphic::Manager::get().newInstance(rVectorGraphicDataPtr))
+    : mxImpGraphic(new ImpGraphic(rVectorGraphicDataPtr))
 {
 }
 
 Graphic::Graphic(const Animation& rAnimation)
-    : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rAnimation))
+    : mxImpGraphic(new ImpGraphic(rAnimation))
 {
 }
 
-Graphic::Graphic(const GDIMetaFile& rMtf)
-    : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rMtf))
+Graphic::Graphic(const GDIMetaFile& rMetaFile)
+    : mxImpGraphic(new ImpGraphic(rMetaFile))
 {
 }
 
@@ -225,19 +225,19 @@ Graphic::Graphic( const css::uno::Reference< 
css::graphic::XGraphic >& rxGraphic
     if( pGraphic )
     {
         if (pGraphic->IsAnimated())
-            mxImpGraphic = 
vcl::graphic::Manager::get().copy(pGraphic->mxImpGraphic);
+            mxImpGraphic = 
std::make_shared<ImpGraphic>(*pGraphic->mxImpGraphic);
         else
             mxImpGraphic = pGraphic->mxImpGraphic;
     }
     else
-        mxImpGraphic = vcl::graphic::Manager::get().newInstance();
+        mxImpGraphic = std::make_shared<ImpGraphic>();
 }
 
 void Graphic::ImplTestRefCount()
 {
     if (mxImpGraphic.use_count() > 1)
     {
-        mxImpGraphic = vcl::graphic::Manager::get().copy(mxImpGraphic);
+        mxImpGraphic = std::make_shared<ImpGraphic>(*mxImpGraphic);
     }
 }
 
@@ -256,7 +256,7 @@ Graphic& Graphic::operator=( const Graphic& rGraphic )
     if( &rGraphic != this )
     {
         if( rGraphic.IsAnimated() )
-            mxImpGraphic = 
vcl::graphic::Manager::get().copy(rGraphic.mxImpGraphic);
+            mxImpGraphic = 
std::make_shared<ImpGraphic>(*rGraphic.mxImpGraphic);
         else
             mxImpGraphic = rGraphic.mxImpGraphic;
     }
diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx
index ceb6c111cf7b..42ea57c1a820 100644
--- a/vcl/source/gdi/impgraph.cxx
+++ b/vcl/source/gdi/impgraph.cxx
@@ -82,12 +82,14 @@ SvStream* ImpGraphic::getSwapFileStream() const
 }
 
 ImpGraphic::ImpGraphic(bool bDefault)
-    : meType(bDefault ? GraphicType::Default : GraphicType::NONE)
+    : MemoryManaged(false)
+    , meType(bDefault ? GraphicType::Default : GraphicType::NONE)
 {
 }
 
 ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
-    : maMetaFile(rImpGraphic.maMetaFile)
+    : MemoryManaged(rImpGraphic)
+    , maMetaFile(rImpGraphic.maMetaFile)
     , maBitmapEx(rImpGraphic.maBitmapEx)
     , maSwapInfo(rImpGraphic.maSwapInfo)
     , mpContext(rImpGraphic.mpContext)
@@ -101,6 +103,8 @@ ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
     , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
     , mbPrepared(rImpGraphic.mbPrepared)
 {
+    updateCurrentSizeInBytes(mnSizeBytes);
+
     // Special case for animations
     if (rImpGraphic.mpAnimation)
     {
@@ -110,7 +114,8 @@ ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
 }
 
 ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
-    : maMetaFile(std::move(rImpGraphic.maMetaFile))
+    : MemoryManaged(rImpGraphic)
+    , maMetaFile(std::move(rImpGraphic.maMetaFile))
     , maBitmapEx(std::move(rImpGraphic.maBitmapEx))
     , maSwapInfo(std::move(rImpGraphic.maSwapInfo))
     , mpAnimation(std::move(rImpGraphic.mpAnimation))
@@ -125,12 +130,15 @@ ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
     , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
     , mbPrepared (rImpGraphic.mbPrepared)
 {
+    updateCurrentSizeInBytes(mnSizeBytes);
+
     rImpGraphic.clear();
     rImpGraphic.mbDummyContext = false;
 }
 
 ImpGraphic::ImpGraphic(std::shared_ptr<GfxLink> xGfxLink, sal_Int32 nPageIndex)
-    : mpGfxLink(std::move(xGfxLink))
+    : MemoryManaged(true)
+    , mpGfxLink(std::move(xGfxLink))
     , meType(GraphicType::Bitmap)
     , mbSwapOut(true)
 {
@@ -140,53 +148,63 @@ ImpGraphic::ImpGraphic(std::shared_ptr<GfxLink> xGfxLink, 
sal_Int32 nPageIndex)
     maSwapInfo.mbIsAnimated = false;
     maSwapInfo.mnAnimationLoopCount = 0;
     maSwapInfo.mnPageIndex = nPageIndex;
+
+    ensureCurrentSizeInBytes();
 }
 
 ImpGraphic::ImpGraphic(GraphicExternalLink aGraphicExternalLink)
-    : meType(GraphicType::Default)
+    : MemoryManaged(true)
+    , meType(GraphicType::Default)
     , maGraphicExternalLink(std::move(aGraphicExternalLink))
 {
+    ensureCurrentSizeInBytes();
 }
 
 ImpGraphic::ImpGraphic(const BitmapEx& rBitmapEx)
-    : maBitmapEx(rBitmapEx)
+    : MemoryManaged(!rBitmapEx.IsEmpty())
+    , maBitmapEx(rBitmapEx)
     , meType(rBitmapEx.IsEmpty() ? GraphicType::NONE : GraphicType::Bitmap)
 {
+    ensureCurrentSizeInBytes();
 }
 
 ImpGraphic::ImpGraphic(const std::shared_ptr<VectorGraphicData>& 
rVectorGraphicDataPtr)
-    : maVectorGraphicData(rVectorGraphicDataPtr)
+    : MemoryManaged(bool(rVectorGraphicDataPtr))
+    , maVectorGraphicData(rVectorGraphicDataPtr)
     , meType(rVectorGraphicDataPtr ? GraphicType::Bitmap : GraphicType::NONE)
 {
+    ensureCurrentSizeInBytes();
 }
 
 ImpGraphic::ImpGraphic(const Animation& rAnimation)
-    : maBitmapEx(rAnimation.GetBitmapEx())
+    : MemoryManaged(true)
+    , maBitmapEx(rAnimation.GetBitmapEx())
     , mpAnimation(std::make_unique<Animation>(rAnimation))
     , meType(GraphicType::Bitmap)
 {
+    ensureCurrentSizeInBytes();
 }
 
 ImpGraphic::ImpGraphic(const GDIMetaFile& rMetafile)
-    : maMetaFile(rMetafile)
+    : MemoryManaged(true)
+    , maMetaFile(rMetafile)
     , meType(GraphicType::GdiMetafile)
 {
+    ensureCurrentSizeInBytes();
 }
 
 ImpGraphic::~ImpGraphic()
 {
-    vcl::graphic::Manager::get().unregisterGraphic(this);
 }
 
 ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
 {
     if( &rImpGraphic != this )
     {
-        sal_Int64 aOldSizeBytes = mnSizeBytes;
-
         maMetaFile = rImpGraphic.maMetaFile;
         meType = rImpGraphic.meType;
         mnSizeBytes = rImpGraphic.mnSizeBytes;
+        updateCurrentSizeInBytes(mnSizeBytes);
 
         maSwapInfo = rImpGraphic.maSwapInfo;
         mpContext = rImpGraphic.mpContext;
@@ -214,7 +232,7 @@ ImpGraphic& ImpGraphic::operator=( const ImpGraphic& 
rImpGraphic )
         maVectorGraphicData = rImpGraphic.maVectorGraphicData;
         maLastUsed = std::chrono::high_resolution_clock::now();
 
-        vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
+        changeExisting(mnSizeBytes);
     }
 
     return *this;
@@ -222,8 +240,6 @@ ImpGraphic& ImpGraphic::operator=( const ImpGraphic& 
rImpGraphic )
 
 ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
 {
-    sal_Int64 aOldSizeBytes = mnSizeBytes;
-
     maMetaFile = std::move(rImpGraphic.maMetaFile);
     meType = rImpGraphic.meType;
     mnSizeBytes = rImpGraphic.mnSizeBytes;
@@ -243,7 +259,7 @@ ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
     rImpGraphic.mbDummyContext = false;
     maLastUsed = std::chrono::high_resolution_clock::now();
 
-    vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
+    changeExisting(mnSizeBytes);
 
     return *this;
 }
@@ -403,9 +419,9 @@ void ImpGraphic::clear()
     // cleanup
     clearGraphics();
     meType = GraphicType::NONE;
-    sal_Int64 nOldSize = mnSizeBytes;
     mnSizeBytes = 0;
-    vcl::graphic::Manager::get().changeExisting(this, nOldSize);
+
+    changeExisting(mnSizeBytes);
     maGraphicExternalLink.msURL.clear();
 }
 
@@ -925,6 +941,14 @@ void ImpGraphic::setPrefMapMode(const MapMode& 
rPrefMapMode)
     setValuesForPrefMapMod(rPrefMapMode);
 }
 
+void ImpGraphic::ensureCurrentSizeInBytes()
+{
+    if (isAvailable())
+        changeExisting(getSizeBytes());
+    else
+        changeExisting(0);
+}
+
 sal_uLong ImpGraphic::getSizeBytes() const
 {
     if (mnSizeBytes > 0)
@@ -1266,8 +1290,6 @@ bool ImpGraphic::swapOut()
 
     bool bResult = false;
 
-    sal_Int64 nByteSize = getSizeBytes();
-
     // We have GfxLink so we have the source available
     if (mpGfxLink && mpGfxLink->IsNative())
     {
@@ -1324,7 +1346,7 @@ bool ImpGraphic::swapOut()
     if (bResult)
     {
         // Signal to manager that we have swapped out
-        vcl::graphic::Manager::get().swappedOut(this, nByteSize);
+        swappedOut(0);
     }
 
     return bResult;
@@ -1332,14 +1354,17 @@ bool ImpGraphic::swapOut()
 
 bool ImpGraphic::ensureAvailable() const
 {
-    auto pThis = const_cast<ImpGraphic*>(this);
-
     bool bResult = true;
 
     if (isSwappedOut())
+    {
+        auto pThis = const_cast<ImpGraphic*>(this);
+        pThis->registerIntoManager();
+
         bResult = pThis->swapIn();
+    }
 
-    pThis->maLastUsed = std::chrono::high_resolution_clock::now();
+    maLastUsed = std::chrono::high_resolution_clock::now();
     return bResult;
 }
 
@@ -1526,7 +1551,7 @@ bool ImpGraphic::swapIn()
 
     if (bReturn)
     {
-        vcl::graphic::Manager::get().swappedIn(this, getSizeBytes());
+        swappedIn(getSizeBytes());
     }
 
     return bReturn;
@@ -1731,4 +1756,22 @@ sal_Int32 ImpGraphic::getPageNumber() const
         return maVectorGraphicData->getPageIndex();
     return -1;
 }
+
+bool ImpGraphic::canReduceMemory() const
+{
+    if (mpContext)
+        return false;
+    return !isSwappedOut();
+}
+
+bool ImpGraphic::reduceMemory()
+{
+    return swapOut();
+}
+
+std::chrono::high_resolution_clock::time_point ImpGraphic::getLastUsed() const
+{
+    return maLastUsed;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/Manager.cxx b/vcl/source/graphic/Manager.cxx
index 4bb4ce79534b..fb316aef153d 100644
--- a/vcl/source/graphic/Manager.cxx
+++ b/vcl/source/graphic/Manager.cxx
@@ -26,8 +26,6 @@
 
 using namespace css;
 
-namespace vcl::graphic
-{
 namespace
 {
 void setupConfigurationValuesIfPossible(sal_Int64& rMemoryLimit,
@@ -51,267 +49,147 @@ void setupConfigurationValuesIfPossible(sal_Int64& 
rMemoryLimit,
 }
 }
 
-Manager& Manager::get()
+namespace vcl::graphic
 {
-    static Manager gStaticManager;
-    return gStaticManager;
-}
-
-Manager::Manager()
-    : mnAllowedIdleTime(10)
-    , mbSwapEnabled(true)
-    , mbReducingGraphicMemory(false)
-    , mnMemoryLimit(300000000)
-    , mnUsedSize(0)
-    , maSwapOutTimer("graphic::Manager maSwapOutTimer")
+MemoryManager::MemoryManager()
+    : maSwapOutTimer("MemoryManager::MemoryManager maSwapOutTimer")
 {
     setupConfigurationValuesIfPossible(mnMemoryLimit, mnAllowedIdleTime, 
mbSwapEnabled);
 
     if (mbSwapEnabled)
     {
-        maSwapOutTimer.SetInvokeHandler(LINK(this, Manager, 
SwapOutTimerHandler));
-        maSwapOutTimer.SetTimeout(10000);
+        maSwapOutTimer.SetInvokeHandler(LINK(this, MemoryManager, 
ReduceMemoryTimerHandler));
+        maSwapOutTimer.SetTimeout(mnTimeout);
         maSwapOutTimer.Start();
     }
 }
 
-void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, 
bool bDropAll)
+MemoryManager& MemoryManager::get()
 {
-    // make a copy of m_pImpGraphicList because if we swap out a svg, the svg
-    // filter may create more temp Graphics which are auto-added to
-    // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
-    // reexport of tdf118346-1.odg
-    o3tl::sorted_vector<ImpGraphic*> aImpGraphicList = m_pImpGraphicList;
-
-    for (ImpGraphic* pEachImpGraphic : aImpGraphicList)
-    {
-        if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7) && !bDropAll)
-            return;
-
-        if (pEachImpGraphic->isSwappedOut())
-            continue;
-
-        sal_Int64 nCurrentGraphicSize = getGraphicSizeBytes(pEachImpGraphic);
-        if (nCurrentGraphicSize > 100000 || bDropAll)
-        {
-            if (!pEachImpGraphic->mpContext)
-            {
-                auto aCurrent = std::chrono::high_resolution_clock::now();
-                auto aDeltaTime = aCurrent - pEachImpGraphic->maLastUsed;
-                auto aSeconds = 
std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime);
-
-                if (aSeconds > mnAllowedIdleTime)
-                {
-                    // unlock because svgio can call back into us
-                    rGuard.unlock();
-                    pEachImpGraphic->swapOut();
-                    rGuard.lock();
-                }
-            }
-        }
-    }
+    static MemoryManager gStaticManager;
+    return gStaticManager;
 }
 
-void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll)
+IMPL_LINK(MemoryManager, ReduceMemoryTimerHandler, Timer*, pTimer, void)
 {
-    // maMutex is locked in callers
-
-    if (!mbSwapEnabled)
-        return;
-
-    if (mnUsedSize < mnMemoryLimit && !bDropAll)
-        return;
-
-    // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to 
odg
-    if (mbReducingGraphicMemory)
-        return;
-
-    mbReducingGraphicMemory = true;
-
-    loopGraphicsAndSwapOut(rGuard, bDropAll);
-
-    sal_Int64 calculatedSize = 0;
-    for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
-    {
-        if (!pEachImpGraphic->isSwappedOut())
-        {
-            calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
-        }
-    }
-
-    if (calculatedSize != mnUsedSize)
-    {
-        assert(rGuard.owns_lock() && rGuard.mutex() == &maMutex);
-        // coverity[missing_lock: FALSE] - as above assert
-        mnUsedSize = calculatedSize;
-    }
-
-    mbReducingGraphicMemory = false;
+    std::unique_lock aGuard(maMutex);
+    pTimer->Stop();
+    reduceMemory(aGuard);
+    pTimer->Start();
 }
 
-void Manager::dropCache()
+void MemoryManager::registerObject(MemoryManaged* pMemoryManaged)
 {
     std::unique_lock aGuard(maMutex);
 
-    reduceGraphicMemory(aGuard, true);
+    // Insert and update the used size (bytes)
+    assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex);
+    // coverity[missing_lock: FALSE] - as above assert
+    mnTotalSize += pMemoryManaged->getCurrentSizeInBytes();
+    maObjectList.insert(pMemoryManaged);
 }
 
-void Manager::dumpState(rtl::OStringBuffer& rState)
+void MemoryManager::unregisterObject(MemoryManaged* pMemoryManaged)
 {
     std::unique_lock aGuard(maMutex);
+    mnTotalSize -= pMemoryManaged->getCurrentSizeInBytes();
+    maObjectList.erase(pMemoryManaged);
+}
 
-    rState.append("
Image Manager items:    ");
-    rState.append(static_cast<sal_Int32>(m_pImpGraphicList.size()));
-    rState.append("    size:   ");
-    rState.append(static_cast<sal_Int64>(mnUsedSize / 1024));
-    rState.append("    kb");
+void MemoryManager::changeExisting(MemoryManaged* pMemoryManaged, sal_Int64 
nNewSize)
+{
+    std::scoped_lock aGuard(maMutex);
+    sal_Int64 nOldSize = pMemoryManaged->getCurrentSizeInBytes();
+    mnTotalSize -= nOldSize;
+    mnTotalSize += nNewSize;
+    pMemoryManaged->setCurrentSizeInBytes(nNewSize);
+}
 
-    for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
-    {
-        pEachImpGraphic->dumpState(rState);
-    }
+void MemoryManager::swappedIn(MemoryManaged* pMemoryManaged, sal_Int64 
nNewSize)
+{
+    changeExisting(pMemoryManaged, nNewSize);
 }
 
-sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic)
+void MemoryManager::swappedOut(MemoryManaged* pMemoryManaged, sal_Int64 
nNewSize)
 {
-    if (!pImpGraphic->isAvailable())
-        return 0;
-    return pImpGraphic->getSizeBytes();
+    changeExisting(pMemoryManaged, nNewSize);
 }
 
-IMPL_LINK(Manager, SwapOutTimerHandler, Timer*, pTimer, void)
+void MemoryManager::reduceAllAndNow()
 {
     std::unique_lock aGuard(maMutex);
-
-    pTimer->Stop();
-    reduceGraphicMemory(aGuard);
-    pTimer->Start();
+    reduceMemory(aGuard, true);
 }
 
-void Manager::registerGraphic(const std::shared_ptr<ImpGraphic>& pImpGraphic)
+void MemoryManager::dumpState(rtl::OStringBuffer& rState)
 {
     std::unique_lock aGuard(maMutex);
 
-    // make some space first
-    if (mnUsedSize > mnMemoryLimit)
-        reduceGraphicMemory(aGuard);
-
-    // Insert and update the used size (bytes)
-    assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex);
-    // coverity[missing_lock: FALSE] - as above assert
-    mnUsedSize += getGraphicSizeBytes(pImpGraphic.get());
-    m_pImpGraphicList.insert(pImpGraphic.get());
-
-    // calculate size of the graphic set
-    sal_Int64 calculatedSize = 0;
-    for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
-    {
-        if (!pEachImpGraphic->isSwappedOut())
-        {
-            calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
-        }
-    }
+    rState.append("
Memory Manager items:   ");
+    rState.append(static_cast<sal_Int32>(maObjectList.size()));
+    rState.append("    size:   ");
+    rState.append(static_cast<sal_Int64>(mnTotalSize / 1024));
+    rState.append("    kb");
 
-    if (calculatedSize != mnUsedSize)
+    for (MemoryManaged* pMemoryManaged : maObjectList)
     {
-        SAL_INFO_IF(calculatedSize != mnUsedSize, "vcl.gdi",
-                    "Calculated size mismatch. Variable size is '"
-                        << mnUsedSize << "' but calculated size is '" << 
calculatedSize << "'");
-        mnUsedSize = calculatedSize;
+        pMemoryManaged->dumpState(rState);
     }
 }
 
-void Manager::unregisterGraphic(ImpGraphic* pImpGraphic)
+void MemoryManager::reduceMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll)
 {
-    std::scoped_lock aGuard(maMutex);
-
-    mnUsedSize -= getGraphicSizeBytes(pImpGraphic);
-    m_pImpGraphicList.erase(pImpGraphic);
-}
+    // maMutex is locked in callers
 
-std::shared_ptr<ImpGraphic> Manager::copy(std::shared_ptr<ImpGraphic> const& 
rImpGraphicPtr)
-{
-    auto pReturn = std::make_shared<ImpGraphic>(*rImpGraphicPtr);
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    if (!mbSwapEnabled)
+        return;
 
-std::shared_ptr<ImpGraphic> Manager::newInstance()
-{
-    auto pReturn = std::make_shared<ImpGraphic>();
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    if (mnTotalSize < mnMemoryLimit && !bDropAll)
+        return;
 
-std::shared_ptr<ImpGraphic> Manager::newInstance(std::shared_ptr<GfxLink> 
const& rGfxLink,
-                                                 sal_Int32 nPageIndex)
-{
-    auto pReturn = std::make_shared<ImpGraphic>(rGfxLink, nPageIndex);
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to 
odg
+    if (mbReducingGraphicMemory)
+        return;
 
-std::shared_ptr<ImpGraphic> Manager::newInstance(const BitmapEx& rBitmapEx)
-{
-    auto pReturn = std::make_shared<ImpGraphic>(rBitmapEx);
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    mbReducingGraphicMemory = true;
 
-std::shared_ptr<ImpGraphic> Manager::newInstance(const Animation& rAnimation)
-{
-    auto pReturn = std::make_shared<ImpGraphic>(rAnimation);
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    loopAndReduceMemory(rGuard, bDropAll);
 
-std::shared_ptr<ImpGraphic>
-Manager::newInstance(const std::shared_ptr<VectorGraphicData>& 
rVectorGraphicDataPtr)
-{
-    auto pReturn = std::make_shared<ImpGraphic>(rVectorGraphicDataPtr);
-    registerGraphic(pReturn);
-    return pReturn;
+    mbReducingGraphicMemory = false;
 }
 
-std::shared_ptr<ImpGraphic> Manager::newInstance(const GDIMetaFile& rMetaFile)
+void MemoryManager::loopAndReduceMemory(std::unique_lock<std::mutex>& rGuard, 
bool bDropAll)
 {
-    auto pReturn = std::make_shared<ImpGraphic>(rMetaFile);
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    // make a copy of m_pImpGraphicList because if we swap out a svg, the svg
+    // filter may create more temp Graphics which are auto-added to
+    // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
+    // reexport of tdf118346-1.odg
 
-std::shared_ptr<ImpGraphic> Manager::newInstance(const GraphicExternalLink& 
rGraphicLink)
-{
-    auto pReturn = std::make_shared<ImpGraphic>(rGraphicLink);
-    registerGraphic(pReturn);
-    return pReturn;
-}
+    o3tl::sorted_vector<MemoryManaged*> aObjectListCopy = maObjectList;
 
-void Manager::swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
-{
-    std::scoped_lock aGuard(maMutex);
-    if (pImpGraphic)
+    for (MemoryManaged* pMemoryManaged : aObjectListCopy)
     {
-        mnUsedSize += nSizeBytes;
-    }
-}
+        if (!pMemoryManaged->canReduceMemory())
+            continue;
 
-void Manager::swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
-{
-    std::scoped_lock aGuard(maMutex);
-    if (pImpGraphic)
-    {
-        mnUsedSize -= nSizeBytes;
+        sal_Int64 nCurrentSizeInBytes = 
pMemoryManaged->getCurrentSizeInBytes();
+        if (nCurrentSizeInBytes > mnSmallFrySize || bDropAll) // ignore 
small-fry
+        {
+            auto aCurrent = std::chrono::high_resolution_clock::now();
+            auto aDeltaTime = aCurrent - pMemoryManaged->getLastUsed();
+            auto aSeconds = 
std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime);
+
+            if (aSeconds > mnAllowedIdleTime)
+            {
+                // unlock because svgio can call back into us
+                rGuard.unlock();
+                pMemoryManaged->reduceMemory();
+                rGuard.lock();
+            }
+        }
     }
 }
 
-void Manager::changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 
nOldSizeBytes)
-{
-    std::scoped_lock aGuard(maMutex);
-
-    mnUsedSize -= nOldSizeBytes;
-    mnUsedSize += getGraphicSizeBytes(pImpGraphic);
-}
 } // end vcl::graphic
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to