Commit 70c554aa authored by Dmitry Kazakov's avatar Dmitry Kazakov

Added KisNode::accessRect() concept

It is used by clone layers, because they may read outside expected
area of image and cause flickering. So this accessRect() method warns
the scheduler about this fact, so no simultaneous access to the image
happens.
parent 5f715372
......@@ -166,7 +166,7 @@ protected:
}
inline void clear() {
m_resultAccessRect = /*m_resultChangeRect =*/
m_resultAccessRect = m_resultNeedRect = /*m_resultChangeRect =*/
m_childNeedRect = m_lastNeedRect = QRect();
m_needRectVaries = m_changeRectVaries = false;
......@@ -219,7 +219,7 @@ protected:
if(!isLayer(node)) return;
if(m_mergeTask.isEmpty())
m_resultAccessRect = m_childNeedRect =
m_resultAccessRect = m_resultNeedRect = m_childNeedRect =
m_lastNeedRect = m_resultChangeRect;
QRect currentNeedRect;
......@@ -232,6 +232,9 @@ protected:
pushJob(node, position, m_lastNeedRect);
//else /* Why push empty rect? */;
m_resultAccessRect |= node->accessRect(m_lastNeedRect,
getPositionToFilthy(position));
m_lastNeedRect = node->needRect(m_lastNeedRect,
getPositionToFilthy(position));
m_lastNeedRect = cropThisRect(m_lastNeedRect);
......@@ -240,6 +243,10 @@ protected:
else if(position & (N_BELOW_FILTHY | N_FILTHY_PROJECTION)) {
if(!m_lastNeedRect.isEmpty()) {
pushJob(node, position, m_lastNeedRect);
m_resultAccessRect |= node->accessRect(m_lastNeedRect,
getPositionToFilthy(position));
m_lastNeedRect = node->needRect(m_lastNeedRect,
getPositionToFilthy(position));
m_lastNeedRect = cropThisRect(m_lastNeedRect);
......@@ -251,8 +258,8 @@ protected:
}
if(!m_needRectVaries)
m_needRectVaries = m_resultAccessRect != m_lastNeedRect;
m_resultAccessRect |= m_lastNeedRect;
m_needRectVaries = m_resultNeedRect != m_lastNeedRect;
m_resultNeedRect |= m_lastNeedRect;
}
virtual void adjustMasksChangeRect(KisNodeSP firstMask) {
......@@ -301,6 +308,7 @@ private:
* data for a successful merge operation.
*/
QRect m_resultAccessRect;
QRect m_resultNeedRect;
QRect m_resultChangeRect;
bool m_needRectVaries;
bool m_changeRectVaries;
......
......@@ -159,6 +159,17 @@ QRect KisCloneLayer::exactBounds() const
return projectionDevice->exactBounds();
}
QRect KisCloneLayer::accessRect(const QRect &rect, PositionToFilthy pos) const
{
QRect resultRect = rect;
if(pos & (N_FILTHY_PROJECTION | N_FILTHY) && (m_d->x || m_d->y)) {
resultRect |= rect.translated(-m_d->x, -m_d->y);
}
return resultRect;
}
bool KisCloneLayer::accept(KisNodeVisitor & v)
{
return v.visit(this);
......
......@@ -84,6 +84,8 @@ public:
/// Returns the exact bounds of where the actual data resides in this layer
QRect exactBounds() const;
QRect accessRect(const QRect &rect, PositionToFilthy pos) const;
bool accept(KisNodeVisitor &);
void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter);
......
......@@ -109,6 +109,12 @@ QRect KisNode::changeRect(const QRect &rect, PositionToFilthy pos) const
return rect;
}
QRect KisNode::accessRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
return rect;
}
void KisNode::setSystemLocked(bool l, bool update)
{
KisBaseNode::setSystemLocked(l, update);
......
......@@ -135,9 +135,31 @@ public:
* Some filters need pixels outside the current processing rect to
* compute the new value (for instance, convolution filters)
* See \ref changeRect
* See \ref accessRect
*/
virtual QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
/**
* Shows the area of image, that may be accessed during accessing
* the node.
*
* Example. You have a layer that needs to prepare some rect on a
* projection, say expectedRect. To perform this, the projection
* of all the layers below of the size needRect(expectedRect)
* should be calculeated by the merger beforehand and the layer
* will access some other area of image inside the rect
* accessRect(expectedRect) during updateProjection call.
*
* This knowledge about real access rect of a node is used by the
* scheduler to avoid collisions between two multithreaded updaters
* and so avoid flickering of the image.
*
* Currently, this method has nondefault value for shifted clone
* layers only.
*/
virtual QRect accessRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
virtual void setSystemLocked(bool l, bool update = true);
public: // Graph methods
......
......@@ -190,9 +190,9 @@ void KisWalkersTest::verifyResult(KisBaseRectsWalker &walker, QStringList refere
qDebug() << "Result AR:\t" << walker.accessRect();
#endif
QVERIFY(walker.accessRect() == accessRect);
QVERIFY(walker.changeRectVaries() == changeRectVaries);
QVERIFY(walker.needRectVaries() == needRectVaries);
QCOMPARE(walker.accessRect(), accessRect);
QCOMPARE(walker.changeRectVaries(), changeRectVaries);
QCOMPARE(walker.needRectVaries(), needRectVaries);
}
......@@ -365,6 +365,66 @@ void KisWalkersTest::testMergeVisiting()
}
/*
+------------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| cplxacc 1 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+------------+
*/
void KisWalkersTest::testComplexAccessVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
KisLayerSP complexAccess = new ComplexAccessLayer(image, "cplxacc1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(complexAccess, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,cplxacc1,paint3,cplx1,paint2");
QStringList orderList = order.split(",");
QRect accessRect = QRect(-7,-7,44,44) | QRect(0,0,30,30).translated(70,0);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
}
class TestingRefreshSubtreeWalker : public KisRefreshSubtreeWalker
{
public:
......
......@@ -197,6 +197,35 @@ public:
}
};
class ComplexAccessLayer : public ComplexRectsLayer
{
Q_OBJECT
public:
ComplexAccessLayer(KisImageWSP image, const QString & name, quint8 opacity)
: ComplexRectsLayer(image, name, opacity) {
}
QRect accessRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const {
Q_UNUSED(pos);
const qint32 delta = 70;
return rect.translated(delta, 0);
}
QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const {
Q_UNUSED(pos);
return rect;
}
QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const {
Q_UNUSED(pos);
return rect;
}
};
class KisBaseRectsWalker;
class KisWalkersTest : public QObject
......@@ -206,6 +235,7 @@ class KisWalkersTest : public QObject
private slots:
void testUsualVisiting();
void testMergeVisiting();
void testComplexAccessVisiting();
void testRefreshSubtreeVisiting();
void testFullRefreshVisiting();
void testCachedVisiting();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment