tdf#131595 Improve drawinglayer flushing mechanism.

Which dramatically speeds up switching between sheets in
the referenced document.

The problem here is that we have a timer for each buffered primitive.
And we have a lot of primitives here.
And the TimerManager does not scale well to lots and lots of timer,
because it uses a linked list.

So this change modifies the flushing mechanism, trading off some
precision for some speed.

Change-Id: I66a744f06af9b08d4e9b797d11db8d22f4060789
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182876
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
This commit is contained in:
Noel Grandin
2025-03-17 07:15:15 +01:00
committed by Noel Grandin
parent f382c1a267
commit 8a17b7f0a6
16 changed files with 270 additions and 214 deletions
-2
View File
@@ -109,7 +109,6 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/bitmapprimitive2d \
drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D \
drawinglayer/source/primitive2d/borderlineprimitive2d \
drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \
drawinglayer/source/primitive2d/controlprimitive2d \
drawinglayer/source/primitive2d/cropprimitive2d \
drawinglayer/source/primitive2d/discretebitmapprimitive2d \
@@ -125,7 +124,6 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/graphicprimitivehelper2d \
drawinglayer/source/primitive2d/graphicprimitive2d \
drawinglayer/source/primitive2d/gridprimitive2d \
drawinglayer/source/primitive2d/groupprimitive2d \
drawinglayer/source/primitive2d/helplineprimitive2d \
drawinglayer/source/primitive2d/hiddengeometryprimitive2d \
drawinglayer/source/primitive2d/invertprimitive2d \
+3
View File
@@ -46,7 +46,10 @@ $(eval $(call gb_Library_use_custom_headers,drawinglayercore,\
$(eval $(call gb_Library_add_exception_objects,drawinglayercore,\
drawinglayer/source/primitive2d/baseprimitive2d \
drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D \
drawinglayer/source/primitive2d/BufferedDecompositionFlusher \
drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \
drawinglayer/source/primitive2d/Primitive2DContainer \
drawinglayer/source/primitive2d/groupprimitive2d \
drawinglayer/source/primitive2d/Tools \
drawinglayer/source/geometry/viewinformation2d \
))
@@ -0,0 +1,119 @@
/* -*- 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 <sal/config.h>
#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
#include <tools/lazydelete.hxx>
namespace drawinglayer::primitive2d
{
/**
This is a "garbage collection" approach to flushing.
We store entries in a set. Every 2 seconds, we scan the set for entries that have not
been used for 10 seconds or more, and if so, we flush the buffer primitives in those entries.
This mechanism is __deliberately__ not perfect.
Sometimes things will be flushed a little too soon, sometimes things will wait a little too long,
since we only have a granularity of 2 seconds.
But what is gains from not being perfect, is scalability.
It is very simple, scales to lots and lots of primitives without needing lots of timers, and performs
very little work in the common case.
*/
static BufferedDecompositionFlusher* getInstance()
{
static tools::DeleteRtlReferenceOnDeinit<BufferedDecompositionFlusher> gaTimer(
new BufferedDecompositionFlusher);
return gaTimer.get().get();
}
// static
void BufferedDecompositionFlusher::update(const BufferedDecompositionPrimitive2D* p)
{
if (auto flusher = getInstance())
flusher->updateImpl(p);
}
// static
void BufferedDecompositionFlusher::update(const BufferedDecompositionGroupPrimitive2D* p)
{
if (auto flusher = getInstance())
flusher->updateImpl(p);
}
BufferedDecompositionFlusher::BufferedDecompositionFlusher()
{
setRemainingTime(salhelper::TTimeValue(2, 0));
start();
}
void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionPrimitive2D* p)
{
std::unique_lock l(maMutex);
maRegistered1.insert(const_cast<BufferedDecompositionPrimitive2D*>(p));
}
void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionGroupPrimitive2D* p)
{
std::unique_lock l(maMutex);
maRegistered2.insert(const_cast<BufferedDecompositionGroupPrimitive2D*>(p));
}
void SAL_CALL BufferedDecompositionFlusher::onShot()
{
auto aNow = std::chrono::steady_clock::now();
std::vector<rtl::Reference<BufferedDecompositionPrimitive2D>> aRemoved1;
std::vector<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> aRemoved2;
{
std::unique_lock l(maMutex);
for (auto it = maRegistered1.begin(); it != maRegistered1.end();)
{
if (aNow - (*it)->maLastAccess > std::chrono::seconds(10))
{
aRemoved1.push_back(*it);
it = maRegistered1.erase(it);
}
else
++it;
}
for (auto it = maRegistered2.begin(); it != maRegistered2.end();)
{
if (aNow - (*it)->maLastAccess > std::chrono::seconds(10))
{
aRemoved2.push_back(*it);
it = maRegistered2.erase(it);
}
else
++it;
}
}
for (const auto& r : aRemoved1)
r->setBuffered2DDecomposition(nullptr);
for (const auto& r : aRemoved2)
r->setBuffered2DDecomposition(Primitive2DContainer{});
setRemainingTime(salhelper::TTimeValue(2, 0));
start();
}
} // end of namespace drawinglayer::primitive2d
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@@ -21,140 +21,80 @@
#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)
{
}
void clearCallback() { pCustomer = nullptr; }
protected:
virtual void SAL_CALL onShot() override;
};
void SAL_CALL LocalCallbackTimer::onShot()
{
if (nullptr != pCustomer)
flushBufferedDecomposition(*pCustomer);
}
}
#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
namespace drawinglayer::primitive2d
{
void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D& rTarget)
{
rTarget.acquire();
rTarget.setBuffered2DDecomposition(Primitive2DContainer());
rTarget.release();
}
bool BufferedDecompositionGroupPrimitive2D::hasBuffered2DDecomposition() const
{
if (0 != maCallbackSeconds)
{
std::lock_guard Guard(maCallbackLock);
if (!mbFlushOnTimer)
return !maBuffered2DDecomposition.empty();
}
else
{
std::lock_guard Guard(maCallbackLock);
return !maBuffered2DDecomposition.empty();
}
}
void BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&& rNew)
{
if (0 == maCallbackSeconds)
if (!mbFlushOnTimer)
{
// no flush used, just set
maBuffered2DDecomposition = std::move(rNew);
return;
}
if (maCallbackTimer.is())
else
{
if (rNew.empty())
{
// stop timer
maCallbackTimer->stop();
}
else
{
// decomposition changed, touch
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
if (!maCallbackTimer->isTicking())
maCallbackTimer->start();
}
}
else if (!rNew.empty())
{
// decomposition defined/set/changed, init & start timer
maCallbackTimer.set(new LocalCallbackTimer(*this));
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
maCallbackTimer->start();
}
// decomposition changed, touch
maLastAccess = std::chrono::steady_clock::now();
BufferedDecompositionFlusher::update(this);
// tdf#158913 need to secure change when flush/multithreading is in use
std::lock_guard Guard(maCallbackLock);
maBuffered2DDecomposition = std::move(rNew);
// tdf#158913 need to secure change when flush/multithreading is in use
std::lock_guard Guard(maCallbackLock);
maBuffered2DDecomposition = std::move(rNew);
}
}
BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D(
Primitive2DContainer&& aChildren)
: GroupPrimitive2D(std::move(aChildren))
, maCallbackTimer()
, maCallbackLock()
, maCallbackSeconds(0)
, mbFlushOnTimer(false)
{
}
BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D()
{
if (maCallbackTimer.is())
{
// no more decomposition, end callback
static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback();
maCallbackTimer->stop();
}
}
BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D() {}
void BufferedDecompositionGroupPrimitive2D::get2DDecomposition(
Primitive2DDecompositionVisitor& rVisitor,
const geometry::ViewInformation2D& rViewInformation) const
{
if (!hasBuffered2DDecomposition())
{
Primitive2DContainer aNewSequence;
create2DDecomposition(aNewSequence, rViewInformation);
const_cast<BufferedDecompositionGroupPrimitive2D*>(this)->setBuffered2DDecomposition(
std::move(aNewSequence));
}
if (0 == maCallbackSeconds)
if (!mbFlushOnTimer)
{
// no flush/multithreading is in use, just call
if (maBuffered2DDecomposition.empty())
create2DDecomposition(maBuffered2DDecomposition, rViewInformation);
rVisitor.visit(maBuffered2DDecomposition);
return;
}
// decomposition was used, touch/restart time
if (maCallbackTimer)
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
// tdf#158913 need to secure 'visit' when flush/multithreading is in use,
// so that the local non-ref-Counted instance of the decomposition gets not
// manipulated (e.g. deleted)
std::lock_guard Guard(maCallbackLock);
rVisitor.visit(maBuffered2DDecomposition);
else
{
// tdf#158913 need to secure 'visit' when flush/multithreading is in use,
// so that the local non-ref-Counted instance of the decomposition gets not
// manipulated (e.g. deleted)
Primitive2DContainer xTmp;
{
// only hold the lock for long enough to get a valid reference
std::lock_guard Guard(maCallbackLock);
maLastAccess = std::chrono::steady_clock::now();
if (maBuffered2DDecomposition.empty())
{
create2DDecomposition(maBuffered2DDecomposition, rViewInformation);
BufferedDecompositionFlusher::update(this);
}
xTmp = maBuffered2DDecomposition;
}
rVisitor.visit(xTmp);
}
}
} // end of namespace drawinglayer::primitive2d
@@ -21,136 +21,79 @@
#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)
{
}
void clearCallback() { pCustomer = nullptr; }
protected:
virtual void SAL_CALL onShot() override;
};
void SAL_CALL LocalCallbackTimer::onShot()
{
if (nullptr != pCustomer)
flushBufferedDecomposition(*pCustomer);
}
}
#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
namespace drawinglayer::primitive2d
{
void flushBufferedDecomposition(BufferedDecompositionPrimitive2D& rTarget)
{
rTarget.acquire();
rTarget.setBuffered2DDecomposition(nullptr);
rTarget.release();
}
bool BufferedDecompositionPrimitive2D::hasBuffered2DDecomposition() const
{
if (0 != maCallbackSeconds)
if (!mbFlushOnTimer)
return maBuffered2DDecomposition.is();
else
{
std::lock_guard Guard(maCallbackLock);
return maBuffered2DDecomposition.is();
}
else
return maBuffered2DDecomposition.is();
}
void BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DReference rNew)
{
if (0 == maCallbackSeconds)
if (!mbFlushOnTimer)
{
// no flush used, just set
maBuffered2DDecomposition = std::move(rNew);
return;
}
if (maCallbackTimer.is())
else
{
if (!rNew)
{
// stop timer
maCallbackTimer->stop();
}
else
{
// decomposition changed, touch
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
if (!maCallbackTimer->isTicking())
maCallbackTimer->start();
}
}
else if (rNew)
{
// decomposition defined/set/changed, init & start timer
maCallbackTimer.set(new LocalCallbackTimer(*this));
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
maCallbackTimer->start();
}
// decomposition changed, touch
maLastAccess = std::chrono::steady_clock::now();
BufferedDecompositionFlusher::update(this);
// tdf#158913 need to secure change when flush/multithreading is in use
std::lock_guard Guard(maCallbackLock);
maBuffered2DDecomposition = std::move(rNew);
// tdf#158913 need to secure change when flush/multithreading is in use
std::lock_guard Guard(maCallbackLock);
maBuffered2DDecomposition = std::move(rNew);
}
}
BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D()
: maBuffered2DDecomposition()
, maCallbackTimer()
, maCallbackLock()
, maCallbackSeconds(0)
, mbFlushOnTimer(false)
{
}
BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D()
{
if (maCallbackTimer.is())
{
// no more decomposition, end callback
static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback();
maCallbackTimer->stop();
}
}
BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D() {}
void BufferedDecompositionPrimitive2D::get2DDecomposition(
Primitive2DDecompositionVisitor& rVisitor,
const geometry::ViewInformation2D& rViewInformation) const
{
if (!hasBuffered2DDecomposition())
{
Primitive2DReference aNew = create2DDecomposition(rViewInformation);
const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition(
std::move(aNew));
}
if (0 == maCallbackSeconds)
if (!mbFlushOnTimer)
{
// no flush/multithreading is in use, just call
if (!maBuffered2DDecomposition)
maBuffered2DDecomposition = create2DDecomposition(rViewInformation);
rVisitor.visit(maBuffered2DDecomposition);
return;
}
// decomposition was used, touch/restart time
if (maCallbackTimer)
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
// tdf#158913 need to secure 'visit' when flush/multithreading is in use,
// so that the local non-ref-Counted instance of the decomposition gets not
// manipulated (e.g. deleted)
std::lock_guard Guard(maCallbackLock);
rVisitor.visit(maBuffered2DDecomposition);
else
{
// tdf#158913 need to secure 'visit' when flush/multithreading is in use,
// so that the local non-ref-Counted instance of the decomposition gets not
// manipulated (e.g. deleted)
Primitive2DReference xTmp;
{
// only hold the lock for long enough to get a valid reference
std::lock_guard Guard(maCallbackLock);
maLastAccess = std::chrono::steady_clock::now();
if (!maBuffered2DDecomposition)
{
maBuffered2DDecomposition = create2DDecomposition(rViewInformation);
BufferedDecompositionFlusher::update(this);
}
xTmp = maBuffered2DDecomposition;
}
rVisitor.visit(xTmp);
}
}
} // end of namespace drawinglayer::primitive2d
@@ -44,7 +44,7 @@ GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, double fRadius,
, maLastClippedRange()
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(15);
activateFlushOnTimer();
}
bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -177,7 +177,7 @@ GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
, maGraphicAttr(rGraphicAttr)
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(20);
activateFlushOnTimer();
}
GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
@@ -186,7 +186,7 @@ GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
, maGraphicObject(rGraphicObject)
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(20);
activateFlushOnTimer();
}
bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -91,7 +91,7 @@ namespace drawinglayer::primitive2d
maMetaFile(rMetaFile)
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(20);
activateFlushOnTimer();
}
bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -633,7 +633,7 @@ namespace drawinglayer::primitive2d
mfOldDiscreteSizeY(0.0)
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(45);
activateFlushOnTimer();
}
bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -51,7 +51,7 @@ ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix aShadowTransform,
, maLastClippedRange()
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(15);
activateFlushOnTimer();
}
bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -40,7 +40,7 @@ SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&&
, maLastClippedRange()
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(15);
activateFlushOnTimer();
}
bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -0,0 +1,49 @@
/* -*- 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 <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
#include <salhelper/timer.hxx>
#include <unordered_set>
namespace drawinglayer::primitive2d
{
class BufferedDecompositionFlusher : public salhelper::Timer
{
public:
static void update(const BufferedDecompositionPrimitive2D*);
static void update(const BufferedDecompositionGroupPrimitive2D*);
BufferedDecompositionFlusher();
private:
virtual void SAL_CALL onShot() override;
void updateImpl(const BufferedDecompositionPrimitive2D*);
void updateImpl(const BufferedDecompositionGroupPrimitive2D*);
std::mutex maMutex;
std::unordered_set<rtl::Reference<BufferedDecompositionPrimitive2D>> maRegistered1;
std::unordered_set<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> maRegistered2;
};
} // end of namespace drawinglayer::primitive2d
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@@ -21,10 +21,12 @@
#include <drawinglayer/drawinglayerdllapi.h>
#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <salhelper/timer.hxx>
#include <chrono>
namespace drawinglayer::primitive2d
{
class BufferedDecompositionFlusher;
/** BufferedDecompositionGroupPrimitive2D class
Baseclass for all C++ implementations which are derived from GroupPrimitive2D
@@ -33,20 +35,20 @@ namespace drawinglayer::primitive2d
For discussion please refer to BufferedDecompositionPrimitive2D, this is the same
but for GroupPrimitive2D which want to buffer their decomposition
*/
class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) BufferedDecompositionGroupPrimitive2D
class UNLESS_MERGELIBS(DRAWINGLAYERCORE_DLLPUBLIC) BufferedDecompositionGroupPrimitive2D
: public GroupPrimitive2D
{
private:
// exclusive helper for Primitive2DFlusher
friend void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D&);
friend class BufferedDecompositionFlusher;
/// a sequence used for buffering the last create2DDecomposition() result
Primitive2DContainer maBuffered2DDecomposition;
mutable Primitive2DContainer maBuffered2DDecomposition;
/// offer callback mechanism to flush buffered content timer-based
::rtl::Reference<::salhelper::Timer> maCallbackTimer;
mutable std::mutex maCallbackLock;
sal_uInt16 maCallbackSeconds;
mutable std::chrono::time_point<std::chrono::steady_clock> maLastAccess;
bool mbFlushOnTimer;
protected:
/// identical to BufferedDecompositionPrimitive2D, see there please
@@ -62,7 +64,7 @@ protected:
// 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; }
void activateFlushOnTimer() { mbFlushOnTimer = true; }
public:
/// constructor/destructor. For GroupPrimitive2D we need the child parameter, too.
@@ -22,7 +22,7 @@
#include <drawinglayer/drawinglayerdllapi.h>
#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <salhelper/timer.hxx>
#include <chrono>
namespace drawinglayer::geometry
{
@@ -31,6 +31,8 @@ class ViewInformation2D;
namespace drawinglayer::primitive2d
{
class BufferedDecompositionFlusher;
/** BufferedDecompositionPrimitive2D class
Baseclass for all C++ implementations of css::graphic::XPrimitive2D
@@ -64,15 +66,15 @@ class DRAWINGLAYERCORE_DLLPUBLIC BufferedDecompositionPrimitive2D : public BaseP
{
private:
// exclusive helper for Primitive2DFlusher
friend void flushBufferedDecomposition(BufferedDecompositionPrimitive2D&);
friend class BufferedDecompositionFlusher;
/// a sequence used for buffering the last create2DDecomposition() result
Primitive2DReference maBuffered2DDecomposition;
mutable Primitive2DReference maBuffered2DDecomposition;
/// offer callback mechanism to flush buffered content timer-based
::rtl::Reference<::salhelper::Timer> maCallbackTimer;
mutable std::mutex maCallbackLock;
sal_uInt16 maCallbackSeconds;
mutable std::chrono::time_point<std::chrono::steady_clock> maLastAccess;
bool mbFlushOnTimer;
protected:
/** access methods to maBuffered2DDecomposition. The usage of this methods may allow
@@ -90,7 +92,7 @@ protected:
// 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; }
void activateFlushOnTimer() { mbFlushOnTimer = true; }
public:
// constructor/destructor
@@ -60,7 +60,7 @@ namespace drawinglayer::primitive2d
- ModifiedColorPrimitive2D (for a stack of color modifications)
- TransformPrimitive2D (for a transformation stack)
*/
class DRAWINGLAYER_DLLPUBLIC GroupPrimitive2D : public BasePrimitive2D
class DRAWINGLAYERCORE_DLLPUBLIC GroupPrimitive2D : public BasePrimitive2D
{
private:
/// the children. Declared private since this shall never be changed at all after construction
@@ -151,7 +151,7 @@ SdrGrafPrimitive2D::SdrGrafPrimitive2D(
, mbPlaceholderImage(bPlaceholderImage)
{
// activate callback to flush buffered decomposition content
setCallbackSeconds(20);
activateFlushOnTimer();
// reset some values from GraphicAttr which are part of transformation already
maGraphicAttr.SetRotation(0_deg10);