drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx  |   
66 +++++++++
 drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx       |   
73 +++++++++-
 drawinglayer/source/primitive2d/glowprimitive2d.cxx                        |   
 2 
 drawinglayer/source/primitive2d/graphicprimitive2d.cxx                     |   
 4 
 drawinglayer/source/primitive2d/metafileprimitive2d.cxx                    |   
 2 
 drawinglayer/source/primitive2d/sceneprimitive2d.cxx                       |   
 5 
 drawinglayer/source/primitive2d/shadowprimitive2d.cxx                      |   
 2 
 drawinglayer/source/primitive2d/softedgeprimitive2d.cxx                    |   
 2 
 include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx |   
24 ++-
 include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx      |   
27 ++-
 svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx                   |   
 2 
 svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx                          |   
 3 
 12 files changed, 193 insertions(+), 19 deletions(-)

New commits:
commit 0d512011a695da8dafe9467ec0ff7f564dfe8c88
Author:     Armin Le Grand (allotropia) <[email protected]>
AuthorDate: Mon Dec 18 18:23:37 2023 +0100
Commit:     Armin Le Grand <[email protected]>
CommitDate: Tue Dec 19 11:04:08 2023 +0100

    Add flush mechanism to buffered Primitives
    
    There are the classes BufferedDecompositionPrimitive2D
    and BufferedDecompositionGroupPrimitive2D that offer
    a unified mechanism to preserve/buffer the decomposition
    of a Primitive, independent of the complexity. This may
    include e.g. the pixel representation of a 3D Scene,
    for Charts/CustomShapes and others.
    
    Until now these were refreshed when the objects change,
    or when the buffered decomposition decides that it is
    necessary, but there was no general mechanism in place
    to clean these up while the Primitive owning them was
    alive/used. This is not a big thing in Draw/Impress
    since this would only be relevant when zooming deep into
    a SdrPage, so the non-vislible parts would still hold
    data that would not be needed. But for Calc and Writer
    this is more relevant: A 3D Chart on the 1st page of a
    Writer document would be held buffered all the time a
    user would work on pages below that. This is good for
    guaranteeing fast re-visualization when scrolling up
    again, but until then would just block memory.
    
    So I added this general mechanism that allows activating
    flushing timer-based, on Seconds. The default is null
    seconds, thus deactivated. It should only be used for
    Primitives that can get expensive, not for all.
    NOTE: I checked activating for all Primitives to be on
    the safe side, works.
    
    It is now (initially) activated for:
    - GlowPrimitive2D
    - GraphicPrimitive2D
    - MetafilePrimitive2D
    - ScenePrimitive2D
    - ShadowPrimitive2D (softShadow)
    - SoftEdgePrimitive2D
    - SdrCustomShapePrimitive2D
    - SdrGrafPrimitive2D
    
    These are the usual suspects that may use some memory
    to buffer their decomposition (for good reasons, each
    repaint uses it). Other Primitives which may show need
    to be treated this way can easily be added, just by
    calling setCallbackSeconds(s) in their constructor. This
    is true for all Primitives derived from the two classes
    mentioned above.
    
    NOTE: Too short buffering times are *not* useful - e.g.
    for a expensive-to-be-recreated 3D chart representation
    this may not pay out, so I chose times in a way that try
    to respect this.
    
    NOTE: This change needs
    7397fa7cdfc33f5a079df42e4d6cfa59ae9e062d to work
    correctly (thanks to sberg for this). Without this the
    office hangs/does not terminate regularly in trying to
    destroy the 'TimerManager'.
    
    Change-Id: Id4802afcb6d12480bb2935cc1ef67fe443b3b788
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160926
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <[email protected]>

diff --git 
a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx 
b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
index 67f41627718e..4b5f52bbe072 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
@@ -22,11 +22,77 @@
 #include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
 
+namespace
+{
+class LocalCallbackTimer : public salhelper::Timer
+{
+protected:
+    drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D* 
pCustomer;
+
+public:
+    explicit LocalCallbackTimer(
+        drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D& 
rCustomer)
+        : pCustomer(&rCustomer)
+    {
+    }
+
+protected:
+    virtual void SAL_CALL onShot() override;
+};
+
+void SAL_CALL LocalCallbackTimer::onShot() { 
flushBufferedDecomposition(*pCustomer); }
+}
+
 namespace drawinglayer::primitive2d
 {
+void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D& rTarget)
+{
+    rTarget.setBuffered2DDecomposition(Primitive2DContainer());
+}
+
+const Primitive2DContainer&
+BufferedDecompositionGroupPrimitive2D::getBuffered2DDecomposition() const
+{
+    if (0 != maCallbackSeconds && maCallbackTimer.is())
+    {
+        // decomposition was used, touch
+        
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+    }
+
+    return maBuffered2DDecomposition;
+}
+
+void 
BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&&
 rNew)
+{
+    if (0 != maCallbackSeconds)
+    {
+        if (rNew.empty())
+        {
+            // no more decomposition, end callback
+            maCallbackTimer.clear();
+        }
+        else if (maCallbackTimer.is())
+        {
+            // decomposition changed, touch
+            
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+        }
+        else
+        {
+            // decomposition changed, start callback
+            maCallbackTimer.set(new LocalCallbackTimer(*this));
+            
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+            maCallbackTimer->start();
+        }
+    }
+
+    maBuffered2DDecomposition = std::move(rNew);
+}
+
 BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D(
     Primitive2DContainer&& aChildren)
     : GroupPrimitive2D(std::move(aChildren))
+    , maCallbackTimer()
+    , maCallbackSeconds(0)
 {
 }
 
diff --git 
a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx 
b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
index 76fa1a9760b1..53b6f877061b 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
@@ -22,9 +22,80 @@
 #include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
 
+namespace
+{
+class LocalCallbackTimer : public salhelper::Timer
+{
+protected:
+    drawinglayer::primitive2d::BufferedDecompositionPrimitive2D* pCustomer;
+
+public:
+    explicit LocalCallbackTimer(
+        drawinglayer::primitive2d::BufferedDecompositionPrimitive2D& rCustomer)
+        : pCustomer(&rCustomer)
+    {
+    }
+
+protected:
+    virtual void SAL_CALL onShot() override;
+};
+
+void SAL_CALL LocalCallbackTimer::onShot() { 
flushBufferedDecomposition(*pCustomer); }
+}
+
 namespace drawinglayer::primitive2d
 {
-BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() {}
+void flushBufferedDecomposition(BufferedDecompositionPrimitive2D& rTarget)
+{
+    rTarget.setBuffered2DDecomposition(Primitive2DContainer());
+}
+
+const Primitive2DContainer& 
BufferedDecompositionPrimitive2D::getBuffered2DDecomposition() const
+{
+    if (0 != maCallbackSeconds && maCallbackTimer.is())
+    {
+        // decomposition was used, touch
+        
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+    }
+
+    return maBuffered2DDecomposition;
+}
+
+void 
BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&&
 rNew)
+{
+    if (0 != maCallbackSeconds)
+    {
+        if (rNew.empty())
+        {
+            // no more decomposition, end callback
+            maCallbackTimer.clear();
+        }
+        else if (maCallbackTimer.is())
+        {
+            // decomposition changed, touch
+            
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+        }
+        else
+        {
+            // decomposition changed, start callback
+            maCallbackTimer.set(new LocalCallbackTimer(*this));
+            
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+            maCallbackTimer->start();
+        }
+    }
+
+    maBuffered2DDecomposition = std::move(rNew);
+}
+
+BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D()
+    : maBuffered2DDecomposition()
+    , maCallbackTimer()
+    , maCallbackSeconds(0)
+    , mnTransparenceForShadow(0)
+{
+}
+
+BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D() {}
 
 void BufferedDecompositionPrimitive2D::get2DDecomposition(
     Primitive2DDecompositionVisitor& rVisitor,
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index fb1a12fa1421..c843129e7ec6 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -43,6 +43,8 @@ GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, 
double fRadius,
     , mfLastDiscreteGlowRadius(0.0)
     , maLastClippedRange()
 {
+    // activate callback to flush buffered decomposition content
+    setCallbackSeconds(15);
 }
 
 bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx 
b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
index c143c8a83231..c26c76f43b55 100644
--- a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
@@ -174,6 +174,8 @@ 
GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
     , maGraphicObject(rGraphicObject)
     , maGraphicAttr(rGraphicAttr)
 {
+    // activate callback to flush buffered decomposition content
+    setCallbackSeconds(20);
 }
 
 GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
@@ -181,6 +183,8 @@ 
GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
     : maTransform(std::move(aTransform))
     , maGraphicObject(rGraphicObject)
 {
+    // activate callback to flush buffered decomposition content
+    setCallbackSeconds(20);
 }
 
 bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx 
b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
index f229aed520a4..d8c20c1ff72d 100644
--- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -92,6 +92,8 @@ namespace drawinglayer::primitive2d
         :   maMetaFileTransform(std::move(aMetaFileTransform)),
             maMetaFile(rMetaFile)
         {
+            // activate callback to flush buffered decomposition content
+            setCallbackSeconds(20);
         }
 
         bool MetafilePrimitive2D::operator==(const BasePrimitive2D& 
rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx 
b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
index b7f7e72c1b8a..64a18bbf4292 100644
--- a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -618,7 +618,8 @@ namespace drawinglayer::primitive2d
             attribute::SdrLightingAttribute aSdrLightingAttribute,
             basegfx::B2DHomMatrix aObjectTransformation,
             geometry::ViewInformation3D aViewInformation3D)
-        :   mxChildren3D(std::move(aChildren3D)),
+        :   BufferedDecompositionPrimitive2D(),
+            mxChildren3D(std::move(aChildren3D)),
             maSdrSceneAttribute(std::move(aSdrSceneAttribute)),
             maSdrLightingAttribute(std::move(aSdrLightingAttribute)),
             maObjectTransformation(std::move(aObjectTransformation)),
@@ -627,6 +628,8 @@ namespace drawinglayer::primitive2d
             mfOldDiscreteSizeX(0.0),
             mfOldDiscreteSizeY(0.0)
         {
+            // activate callback to flush buffered decomposition content
+            setCallbackSeconds(45);
         }
 
         bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) 
const
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index 8fb2a312266b..5de34c5440b6 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -50,6 +50,8 @@ ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix 
aShadowTransform,
     , mfLastDiscreteBlurRadius(0.0)
     , maLastClippedRange()
 {
+    // activate callback to flush buffered decomposition content
+    setCallbackSeconds(15);
 }
 
 bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx 
b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index 87e60467f1ac..e6f92f312f59 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -39,6 +39,8 @@ SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, 
Primitive2DContainer&&
     , mfLastDiscreteSoftRadius(0.0)
     , maLastClippedRange()
 {
+    // activate callback to flush buffered decomposition content
+    setCallbackSeconds(15);
 }
 
 bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git 
a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx 
b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
index a1cfee38798f..c3bc54e5fca3 100644
--- a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
@@ -21,6 +21,7 @@
 
 #include <drawinglayer/drawinglayerdllapi.h>
 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <salhelper/timer.hxx>
 
 namespace drawinglayer::primitive2d
 {
@@ -35,25 +36,32 @@ namespace drawinglayer::primitive2d
 class DRAWINGLAYER_DLLPUBLIC BufferedDecompositionGroupPrimitive2D : public 
GroupPrimitive2D
 {
 private:
+    // exclusive helper for Primitive2DFlusher
+    friend void 
flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D&);
+
     /// a sequence used for buffering the last create2DDecomposition() result
     Primitive2DContainer maBuffered2DDecomposition;
 
+    /// offer callback mechanism to flush buffered content timer-based
+    ::rtl::Reference<::salhelper::Timer> maCallbackTimer;
+    sal_uInt16 maCallbackSeconds;
+
 protected:
     /// identical to BufferedDecompositionPrimitive2D, see there please
-    const Primitive2DContainer& getBuffered2DDecomposition() const
-    {
-        return maBuffered2DDecomposition;
-    }
-    void setBuffered2DDecomposition(Primitive2DContainer&& rNew)
-    {
-        maBuffered2DDecomposition = std::move(rNew);
-    }
+    const Primitive2DContainer& getBuffered2DDecomposition() const;
+    void setBuffered2DDecomposition(Primitive2DContainer&& rNew);
 
     /// method which is to be used to implement the local decomposition of a 
2D group primitive.
     virtual void
     create2DDecomposition(Primitive2DContainer& rContainer,
                           const geometry::ViewInformation2D& rViewInformation) 
const = 0;
 
+    // when changing from null (which is inactive) to a count of seconds, the
+    // callback mechanism to flush buffered content timer-based will be 
activated.
+    // it is protected since the idea is that this gets called in the 
constructor
+    // of derived classes.
+    void setCallbackSeconds(sal_uInt16 nNew) { maCallbackSeconds = nNew; }
+
 public:
     /// constructor/destructor. For GroupPrimitive2D we need the child 
parameter, too.
     BufferedDecompositionGroupPrimitive2D(Primitive2DContainer&& aChildren);
diff --git 
a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx 
b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
index 8087a6544dc0..68d40e006a6f 100644
--- a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
@@ -22,6 +22,7 @@
 #include <drawinglayer/drawinglayerdllapi.h>
 #include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <salhelper/timer.hxx>
 
 namespace drawinglayer::geometry
 {
@@ -62,35 +63,43 @@ namespace drawinglayer::primitive2d
 class DRAWINGLAYERCORE_DLLPUBLIC BufferedDecompositionPrimitive2D : public 
BasePrimitive2D
 {
 private:
+    // exclusive helper for Primitive2DFlusher
+    friend void flushBufferedDecomposition(BufferedDecompositionPrimitive2D&);
+
     /// a sequence used for buffering the last create2DDecomposition() result
     Primitive2DContainer maBuffered2DDecomposition;
 
+    /// offer callback mechanism to flush buffered content timer-based
+    ::rtl::Reference<::salhelper::Timer> maCallbackTimer;
+    sal_uInt16 maCallbackSeconds;
+
     /// When a shadow wraps a list of primitives, this primitive wants to 
influence the transparency
     /// of the shadow.
-    sal_uInt16 mnTransparenceForShadow = 0;
+    sal_uInt16 mnTransparenceForShadow;
 
 protected:
     /** access methods to maBuffered2DDecomposition. The usage of this methods 
may allow
         later thread-safe stuff to be added if needed. Only to be used by 
getDecomposition()
         implementations for buffering the last decomposition.
      */
-    const Primitive2DContainer& getBuffered2DDecomposition() const
-    {
-        return maBuffered2DDecomposition;
-    }
-    void setBuffered2DDecomposition(Primitive2DContainer&& rNew)
-    {
-        maBuffered2DDecomposition = std::move(rNew);
-    }
+    const Primitive2DContainer& getBuffered2DDecomposition() const;
+    void setBuffered2DDecomposition(Primitive2DContainer&& rNew);
 
     /** method which is to be used to implement the local decomposition of a 
2D primitive. */
     virtual void
     create2DDecomposition(Primitive2DContainer& rContainer,
                           const geometry::ViewInformation2D& rViewInformation) 
const = 0;
 
+    // when changing from null (which is inactive) to a count of seconds, the
+    // callback mechanism to flush buffered content timer-based will be 
activated.
+    // it is protected since the idea is that this gets called in the 
constructor
+    // of derived classes.
+    void setCallbackSeconds(sal_uInt16 nNew) { maCallbackSeconds = nNew; }
+
 public:
     // constructor/destructor
     BufferedDecompositionPrimitive2D();
+    virtual ~BufferedDecompositionPrimitive2D();
 
     /** The getDecomposition default implementation will on demand use 
create2DDecomposition() if
         maBuffered2DDecomposition is empty. It will set 
maBuffered2DDecomposition to this obtained decomposition
diff --git a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx 
b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
index 19717e2eb121..b58274106211 100644
--- a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
+++ b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
@@ -102,6 +102,8 @@ namespace drawinglayer::primitive2d
             mb3DShape(b3DShape),
             maTransform(std::move(aTransform))
         {
+            // activate callback to flush buffered decomposition content
+            setCallbackSeconds(10);
         }
 
         bool SdrCustomShapePrimitive2D::operator==(const BasePrimitive2D& 
rPrimitive) const
diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx 
b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
index 18bef098575a..67d4d03507ed 100644
--- a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
+++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
@@ -133,6 +133,9 @@ SdrGrafPrimitive2D::SdrGrafPrimitive2D(
     , maGraphicObject(rGraphicObject)
     , maGraphicAttr(rGraphicAttr)
 {
+    // activate callback to flush buffered decomposition content
+    setCallbackSeconds(20);
+
     // reset some values from GraphicAttr which are part of transformation 
already
     maGraphicAttr.SetRotation(0_deg10);
 }

Reply via email to