Commit 191011d3 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fixed multiple transform masks problems

This patch implements:

1) KisImage::requestProjectionUpdateNoFilthy() the same as layer->setDirty(),
   but skips regeneration of layer's projection.

2) KisLayer::updateProjection() now accepts not layer's position, but
   a pointer to the filthy node.

3) KisEffectMask::decorateRect() now accepts a position relative to the
   filthy mask/layer calculated inside the layer itself. *Not* the one used
   by KisAsyncMerger.

Known bugs: tested only with affine transformations, that is Free and
            4-point perspective
parent 3b30b2c4
......@@ -223,7 +223,7 @@ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
m_currentProjection,
walker.cropRect());
currentNode->accept(originalVisitor);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
currentNode->updateProjection(applyRect, currentNode);
continue;
}
......@@ -239,18 +239,18 @@ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
if(item.m_position & KisMergeWalker::N_FILTHY) {
DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentNode, applyRect);
currentNode->accept(originalVisitor);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
currentNode->updateProjection(applyRect, walker.startNode());
}
else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) {
DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentNode, applyRect);
if(dependOnLowerNodes(currentNode)) {
currentNode->accept(originalVisitor);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
currentNode->updateProjection(applyRect, currentNode);
}
}
else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) {
DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentNode, applyRect);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
currentNode->updateProjection(applyRect, walker.startNode());
}
else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ {
DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentNode, applyRect);
......
......@@ -32,6 +32,7 @@ class KRITAIMAGE_EXPORT KisBaseRectsWalker : public KisShared
public:
enum UpdateType {
UPDATE,
UPDATE_NO_FILTHY,
FULL_REFRESH,
UNSUPPORTED
};
......
......@@ -242,7 +242,6 @@ QRect KisCloneLayer::accessRect(const QRect &rect, PositionToFilthy pos) const
QRect KisCloneLayer::outgoingChangeRect(const QRect &rect) const
{
return rect.translated(m_d->x, m_d->y);
return rect;
}
bool KisCloneLayer::accept(KisNodeVisitor & v)
......
......@@ -70,9 +70,9 @@ void KisFilterMask::setFilter(KisFilterConfiguration * filterConfig)
QRect KisFilterMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const
PositionToFilthy maskPos) const
{
Q_UNUSED(parentPos);
Q_UNUSED(maskPos);
KisSafeFilterConfigurationSP filterConfig = filter();
......
......@@ -61,7 +61,7 @@ public:
QRect decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const;
PositionToFilthy maskPos) const;
QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
......
......@@ -1491,6 +1491,15 @@ void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &c
}
}
void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect)
{
KIS_ASSERT_RECOVER_RETURN(pseudoFilthy);
if (m_d->scheduler) {
m_d->scheduler->updateProjectionNoFilthy(pseudoFilthy, rc, cropRect);
}
}
void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{
if (m_d->scheduler) {
......
......@@ -709,6 +709,17 @@ public slots:
void refreshGraph(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void initialRefreshGraph();
/**
* Initiate a stack regeneration skipping the recalculation of the
* filthy node's projection.
*
* Works exactly as pseudoFilthy->setDirty() with the only
* exception that pseudoFilthy::updateProjection() will not be
* called. That is used by KisRecalculateTransformMaskJob to avoid
* cyclic dependencies.
*/
void requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect);
/**
* Adds a spontaneous job to the updates queue.
*
......
......@@ -389,10 +389,33 @@ QRect KisLayer::masksNeedRect(const QList<KisEffectMaskSP> &masks,
return needRect;
}
KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion,
KisNodeSP filthy,
KisNodeSP parent)
{
if (parent == filthy || parent != filthy->parent()) {
return KisNode::N_ABOVE_FILTHY;
}
if (nodeInQuestion == filthy) {
return KisNode::N_FILTHY;
}
KisNodeSP node = nodeInQuestion->prevSibling();
while (node) {
if (node == filthy) {
return KisNode::N_ABOVE_FILTHY;
}
node = node->prevSibling();
}
return KisNode::N_BELOW_FILTHY;
}
QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
const KisPaintDeviceSP destination,
const QRect &requestedRect,
PositionToFilthy pos,
KisNodeSP filthyNode,
KisNodeSP lastNode) const
{
Q_ASSERT(source);
......@@ -442,7 +465,8 @@ QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
const QRect maskNeedRect =
applyRects.isEmpty() ? needRect : applyRects.top();
mask->apply(destination, maskApplyRect, maskNeedRect, pos);
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition);
}
Q_ASSERT(applyRects.isEmpty());
} else {
......@@ -460,7 +484,8 @@ QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
QRect maskNeedRect = needRect;
foreach(const KisEffectMaskSP& mask, masks) {
mask->apply(tempDevice, maskApplyRect, maskNeedRect, pos);
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition);
if (!applyRects.isEmpty()) {
maskNeedRect = maskApplyRect;
......@@ -478,7 +503,7 @@ QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
return changeRect;
}
QRect KisLayer::updateProjection(const QRect& rect, PositionToFilthy pos)
QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode)
{
QRect updatedRect = rect;
KisPaintDeviceSP originalDevice = original();
......@@ -495,31 +520,37 @@ QRect KisLayer::updateProjection(const QRect& rect, PositionToFilthy pos)
m_d->safeProjection.getDeviceLazy(originalDevice);
updatedRect = applyMasks(originalDevice, projection,
updatedRect, pos, 0);
updatedRect, filthyNode, 0);
}
}
return updatedRect;
}
/**
* \p rect is a dirty rect in layer's original() coordinates!
*/
void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect, PositionToFilthy pos)
QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect)
{
bool changeRectVaries = false;
QRect changeRect = outgoingChangeRect(rect);
changeRect = masksChangeRect(effectMasks(lastNode), changeRect,
changeRectVaries);
return changeRect;
}
/**
* \p rect is a dirty rect in layer's original() coordinates!
*/
void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect)
{
QRect changeRect = partialChangeRect(lastNode, rect);
KisPaintDeviceSP originalDevice = original();
KIS_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks());
if (!changeRect.isEmpty()) {
applyMasks(originalDevice, projection,
changeRect, pos, lastNode);
changeRect, this, lastNode);
}
}
......
......@@ -83,9 +83,10 @@ public:
* Ask the layer to assemble its data & apply all the effect masks
* to it.
*/
QRect updateProjection(const QRect& rect, PositionToFilthy pos);
QRect updateProjection(const QRect& rect, KisNodeSP filthyNode);
void buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect, PositionToFilthy pos);
QRect partialChangeRect(KisNodeSP lastNode, const QRect& rect);
void buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect);
virtual bool needProjection() const;
......@@ -326,7 +327,7 @@ protected:
QRect applyMasks(const KisPaintDeviceSP source,
const KisPaintDeviceSP destination,
const QRect &requestedRect,
PositionToFilthy pos, KisNodeSP lastNode) const;
KisNodeSP filthyNode, KisNodeSP lastNode) const;
private:
struct Private;
......
......@@ -212,16 +212,16 @@ void KisMask::select(const QRect & rc, quint8 selectedness)
QRect KisMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const
PositionToFilthy maskPos) const
{
Q_UNUSED(src);
Q_UNUSED(dst);
Q_UNUSED(parentPos);
Q_UNUSED(maskPos);
Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors");
return rc;
}
void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy parentPos) const
void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const
{
if (selection()) {
......@@ -232,7 +232,7 @@ void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const Q
KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection);
QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, parentPos);
QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos);
KisPainter gc(projection);
// masks don't have any compositioning
......@@ -248,7 +248,7 @@ void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const Q
cacheDevice->makeCloneFromRough(projection, needRect);
projection->clear(needRect);
decorateRect(cacheDevice, projection, applyRect, parentPos);
decorateRect(cacheDevice, projection, applyRect, maskPos);
m_d->paintDeviceCache.putDevice(cacheDevice);
}
......
......@@ -179,11 +179,11 @@ protected:
* Apply the effect the projection using the mask as a selection.
* Made public in KisEffectMask
*/
void apply(KisPaintDeviceSP projection, const QRect & applyRect, const QRect & needRect, PositionToFilthy parentPos) const;
void apply(KisPaintDeviceSP projection, const QRect & applyRect, const QRect & needRect, PositionToFilthy maskPos) const;
virtual QRect decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const;
PositionToFilthy maskPos) const;
private:
......
......@@ -19,7 +19,8 @@
#include "kis_merge_walker.h"
KisMergeWalker::KisMergeWalker(QRect cropRect)
KisMergeWalker::KisMergeWalker(QRect cropRect, Flags flags)
: m_flags(flags)
{
setCropRect(cropRect);
}
......@@ -30,7 +31,7 @@ KisMergeWalker::~KisMergeWalker()
KisBaseRectsWalker::UpdateType KisMergeWalker::type() const
{
return KisBaseRectsWalker::UPDATE;
return m_flags == DEFAULT ? KisBaseRectsWalker::UPDATE : KisBaseRectsWalker::UPDATE_NO_FILTHY;
}
void KisMergeWalker::startTrip(KisNodeSP startWith)
......@@ -40,7 +41,8 @@ void KisMergeWalker::startTrip(KisNodeSP startWith)
return;
}
visitHigherNode(startWith, N_FILTHY);
visitHigherNode(startWith,
m_flags == DEFAULT ? N_FILTHY : N_ABOVE_FILTHY);
KisNodeSP prevNode = startWith->prevSibling();
if(prevNode)
......@@ -62,7 +64,8 @@ void KisMergeWalker::startTripWithMask(KisNodeSP filthyMask)
else if (parentLayer->parent())
startTrip(parentLayer->parent());
NodePosition positionToFilthy = N_FILTHY_PROJECTION |
NodePosition positionToFilthy =
(m_flags == DEFAULT ? N_FILTHY_PROJECTION : N_ABOVE_FILTHY) |
calculateNodePosition(parentLayer);
registerNeedRect(parentLayer, positionToFilthy);
......
......@@ -30,13 +30,26 @@ class KRITAIMAGE_EXPORT KisMergeWalker : public virtual KisBaseRectsWalker
{
public:
KisMergeWalker(QRect cropRect);
/**
* NO_FILTHY flag notifies the walker that there should be no (!)
* filthy node in the update. It means that the projection() of
* the node is already guaranteed to be ready, we just need to
* update all the higher-level nodes. Used by KisTransformMask
* regeneration code.
*/
enum Flags {
DEFAULT = 0,
NO_FILTHY
};
KisMergeWalker(QRect cropRect, Flags flags = DEFAULT);
virtual ~KisMergeWalker();
UpdateType type() const;
protected:
KisMergeWalker() {}
KisMergeWalker() : m_flags(DEFAULT) {}
/**
* Begins visiting nodes starting with @startWith.
......@@ -64,6 +77,9 @@ private:
* startTrip() one more time.
*/
void visitLowerNode(KisNodeSP node);
private:
const Flags m_flags;
};
......
......@@ -19,6 +19,9 @@
#include "kis_recalculate_transform_mask_job.h"
#include "kis_transform_mask.h"
#include "kis_debug.h"
#include "kis_layer.h"
#include "kis_image.h"
KisRecalculateTransformMaskJob::KisRecalculateTransformMaskJob(KisTransformMaskSP mask)
......@@ -43,5 +46,16 @@ void KisRecalculateTransformMaskJob::run()
if (!m_mask->parent()) return;
m_mask->recaclulateStaticImage();
m_mask->setDirty();
KisLayerSP layer = dynamic_cast<KisLayer*>(m_mask->parent().data());
if (!layer) {
qWarning() << "WARNING: KisRecalculateTransformMaskJob::run() Mask has no parent layer! Skipping projection update!";
return;
}
KisImageSP image = layer->image();
Q_ASSERT(image);
image->requestProjectionUpdateNoFilthy(layer, layer->extent(), image->bounds());
}
......@@ -113,9 +113,25 @@ bool KisSimpleUpdateQueue::processOneJob(KisUpdaterContext &updaterContext)
if (jobAdded) return true;
if (!m_spontaneousJobsList.isEmpty()) {
KisSpontaneousJob *job = m_spontaneousJobsList.takeFirst();
updaterContext.addSpontaneousJob(job);
jobAdded = true;
/**
* WARNING: Please note that this still doesn't guarantee that
* the spontaneous jobs are exclusive, since updates and/or
* strokes can be added after them. The only thing it
* guarantees that two spontaneous jobs will not be executed
* in parallel.
*
* Right now it works as it is. Probably will need to be fixed
* in the future.
*/
qint32 numMergeJobs;
qint32 numStrokeJobs;
updaterContext.getJobsSnapshot(numMergeJobs, numStrokeJobs);
if (!numMergeJobs && !numStrokeJobs) {
KisSpontaneousJob *job = m_spontaneousJobsList.takeFirst();
updaterContext.addSpontaneousJob(job);
jobAdded = true;
}
}
return jobAdded;
......@@ -126,6 +142,11 @@ void KisSimpleUpdateQueue::addUpdateJob(KisNodeSP node, const QRect& rc, const Q
addJob(node, rc, cropRect, KisBaseRectsWalker::UPDATE);
}
void KisSimpleUpdateQueue::addUpdateNoFilthyJob(KisNodeSP node, const QRect& rc, const QRect& cropRect)
{
addJob(node, rc, cropRect, KisBaseRectsWalker::UPDATE_NO_FILTHY);
}
void KisSimpleUpdateQueue::addFullRefreshJob(KisNodeSP node, const QRect& rc, const QRect& cropRect)
{
addJob(node, rc, cropRect, KisBaseRectsWalker::FULL_REFRESH);
......@@ -140,12 +161,15 @@ void KisSimpleUpdateQueue::addJob(KisNodeSP node, const QRect& rc,
KisBaseRectsWalkerSP walker;
if(type == KisBaseRectsWalker::UPDATE) {
walker = new KisMergeWalker(cropRect);
if (type == KisBaseRectsWalker::UPDATE) {
walker = new KisMergeWalker(cropRect, KisMergeWalker::DEFAULT);
}
else /* if(type == KisBaseRectsWalker::FULL_REFRESH) */ {
else if (type == KisBaseRectsWalker::FULL_REFRESH) {
walker = new KisFullRefreshWalker(cropRect);
}
else if (type == KisBaseRectsWalker::UPDATE_NO_FILTHY) {
walker = new KisMergeWalker(cropRect, KisMergeWalker::NO_FILTHY);
}
/* else if(type == KisBaseRectsWalker::UNSUPPORTED) qFatal(); */
walker->collectRects(node, rc);
......
......@@ -40,6 +40,7 @@ public:
void processQueue(KisUpdaterContext &updaterContext);
void addUpdateJob(KisNodeSP node, const QRect& rc, const QRect& cropRect);
void addUpdateNoFilthyJob(KisNodeSP node, const QRect& rc, const QRect& cropRect);
void addFullRefreshJob(KisNodeSP node, const QRect& rc, const QRect& cropRect);
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
......
......@@ -162,7 +162,7 @@ KisPaintDeviceSP KisTransformMask::buildPreviewDevice()
new KisPaintDevice(parentLayer->original()->colorSpace());
QRect requestedRect = parentLayer->original()->exactBounds();
parentLayer->buildProjectionUpToNode(device, this, requestedRect, N_FILTHY_PROJECTION);
parentLayer->buildProjectionUpToNode(device, this, requestedRect);
return device;
}
......@@ -192,12 +192,11 @@ void KisTransformMask::recaclulateStaticImage()
QRect requestedRect = parentLayer->changeRect(parentLayer->original()->exactBounds());
/**
* TODO: If we use buildProjectionUpToNode() here, then the final
* projection of the node will not be ready. But anyway we
* issue setDirty() call after that... So probably
* setDirty() should be avoided somehow?
* Here we use updateProjection() to regenerate the projection of
* the layer and after that a special update call (no-filthy) will
* be issued to pass the changed further through the stack.
*/
parentLayer->updateProjection(requestedRect, N_FILTHY_PROJECTION);
parentLayer->updateProjection(requestedRect, this);
m_d->recalculatingStaticImage = false;
m_d->staticCacheValid = true;
......@@ -206,7 +205,7 @@ void KisTransformMask::recaclulateStaticImage()
QRect KisTransformMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const
PositionToFilthy maskPos) const
{
Q_ASSERT(nodeProgressProxy());
Q_ASSERT_X(src != dst, "KisTransformMask::decorateRect",
......@@ -216,16 +215,19 @@ QRect KisTransformMask::decorateRect(KisPaintDeviceSP &src,
KIS_ASSERT_RECOVER(m_d->params) { return rc; }
if (m_d->params->isHidden()) return rc;
KIS_ASSERT_RECOVER_NOOP(maskPos == N_FILTHY ||
maskPos == N_ABOVE_FILTHY ||
maskPos == N_BELOW_FILTHY);
if (parentPos != N_FILTHY_PROJECTION) {
// TODO: use special filtering callbacks of KisImage instead
// otherwise it doesn't work with two t-masks
if (!m_d->recalculatingStaticImage &&
(maskPos == N_FILTHY || maskPos == N_ABOVE_FILTHY)) {
m_d->staticCacheValid = false;
emit initiateDelayedStaticUpdate();
}
if (m_d->recalculatingStaticImage) {
m_d->staticCacheDevice->clear();
m_d->params->transformDevice(const_cast<KisTransformMask*>(this), src, m_d->staticCacheDevice);
dst->makeCloneFrom(m_d->staticCacheDevice, m_d->staticCacheDevice->extent());
} else if (!m_d->staticCacheValid && m_d->params->isAffine()) {
......@@ -319,20 +321,31 @@ QRect KisTransformMask::needRect(const QRect& rect, PositionToFilthy pos) const
QRect KisTransformMask::extent() const
{
// TODO: collect extent of all the previous siblings otherwise
// updates of two t-mask will not work correctly
QRect rc = KisMask::extent();
return rc | changeRect(rc);
QRect partialChangeRect;
QRect existentProjection;
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
if (parentLayer) {
partialChangeRect = parentLayer->partialChangeRect(const_cast<KisTransformMask*>(this), rc);
existentProjection = parentLayer->projection()->extent();
}
return changeRect(partialChangeRect) | existentProjection;
}
QRect KisTransformMask::exactBounds() const
{
// TODO: collect exactBounds of all the previous siblings
// otherwise updates of two t-mask will not work correctly
QRect rc = KisMask::exactBounds();
return rc | changeRect(rc);
QRect partialChangeRect;
QRect existentProjection;
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
if (parentLayer) {
partialChangeRect = parentLayer->partialChangeRect(const_cast<KisTransformMask*>(this), rc);
existentProjection = parentLayer->projection()->exactBounds();
}
return changeRect(partialChangeRect) | existentProjection;
}
#include "kis_transform_mask.moc"
......@@ -56,7 +56,7 @@ public:
QRect decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const;
PositionToFilthy maskPos) const;
QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
......
......@@ -47,9 +47,9 @@ KisTransparencyMask::~KisTransparencyMask()
QRect KisTransparencyMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const
PositionToFilthy maskPos) const
{
Q_UNUSED(parentPos);
Q_UNUSED(maskPos);
if (src != dst) {
KisPainter gc(dst);
......
......@@ -50,7 +50,7 @@ public:
QRect decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy parentPos) const;
PositionToFilthy maskPos) const;
QIcon icon() const;
bool accept(KisNodeVisitor &v);
void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter);
......
......@@ -141,6 +141,12 @@ void KisUpdateScheduler::updateProjection(KisNodeSP node, const QRect& rc, const
processQueues();
}
void KisUpdateScheduler::updateProjectionNoFilthy(KisNodeSP node, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue->addUpdateNoFilthyJob(node, rc, cropRect);
processQueues();
}
void KisUpdateScheduler::fullRefreshAsync(KisNodeSP root, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue->addFullRefreshJob(root, rc, cropRect);
......
......@@ -121,6 +121,7 @@ public:
void unblockUpdates();
void updateProjection(KisNodeSP node, const QRect& rc, const QRect &cropRect);
void updateProjectionNoFilthy(KisNodeSP node, const QRect& rc, const QRect &cropRect);
void fullRefreshAsync(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void fullRefresh(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
......
......@@ -59,7 +59,7 @@ void KisPaintLayerTest::testProjection()
Q_ASSERT(layer->hasEffectMasks());
// And now we're going to update the projection, but nothing is dirty yet
layer->updateProjection(qimage.rect(), KisNode::N_FILTHY);
layer->updateProjection(qimage.rect(), layer);
// Which also means that the projection is no longer the paint device
QVERIFY(layer->paintDevice().data() != layer->projection().data());
......@@ -68,7 +68,7 @@ void KisPaintLayerTest::testProjection()
layer->setDirty(qimage.rect());
// And now we're going to update the projection, but nothing is dirty yet
layer->updateProjection(qimage.rect(), KisNode::N_FILTHY);
layer->updateProjection(qimage.rect(), layer);
// Which also means that the projection is no longer the paint device
QVERIFY(layer->paintDevice().data() != layer->projection().data());
......@@ -78,7 +78,7 @@ void KisPaintLayerTest::testProjection()
QVERIFY(layer->projection().data() != 0);
// The selection is initially empty, so after an update, all pixels are still visible
layer->updateProjection(qimage.rect(), KisNode::N_FILTHY);
layer->updateProjection(qimage.rect(), layer);
// We've inverted the mask, so now nothing is seen
KisSequentialConstIterator it(layer->projection(), qimage.rect());
......
......@@ -199,15 +199,17 @@ void KisSimpleUpdateQueueTest::testMixingTypes()
queue.addUpdateJob(paintLayer, dirtyRect1, imageRect);
queue.addFullRefreshJob(paintLayer, dirtyRect2, imageRect);
queue.addFullRefreshJob(paintLayer, dirtyRect3, imageRect);
queue.addUpdateNoFilthyJob(paintLayer, dirtyRect1, imageRect);
QCOMPARE(walkersList.size(), 2);
QCOMPARE(walkersList.size(), 3);
QVERIFY(checkWalker(walkersList[0], QRect(0,0,200,200)));
QVERIFY(checkWalker(walkersList[1], QRect(0,0,220,220)));
QVERIFY(checkWalker(walkersList[2], QRect(0,0,200,200)));
QCOMPARE(walkersList[0]->type(), KisBaseRectsWalker::UPDATE);
QCOMPARE(walkersList[1]->type(), KisBaseRectsWalker::FULL_REFRESH);
QCOMPARE(walkersList[2]->type(), KisBaseRectsWalker::UPDATE_NO_FILTHY);
}
void KisSimpleUpdateQueueTest::testSpontaneousJobsCompression()
......
......@@ -471,4 +471,292 @@ void KisTransformMaskTest::testMaskOnCloneLayerWithOffset()
QVERIFY(doPartialTests("clone_offset_complex", p.image, p.layer, clone, mask));
}
#define CHECK_MASK1_TOGGLE
#define CHECK_MASK2_TOGGLE<