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:
committed by
Noel Grandin
parent
f382c1a267
commit
8a17b7f0a6
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user