Commit dcdef2ea authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix lags in Move Tool when using tablet device

Now the Move Tool uses the same FSP-limiting compression as the freehand
tool does. The relevant code has been moved into a special class
KisAsyncronousStrokeUpdateHelper, which implements a logic of issuing
threshold events.

Bascially, all the sources of event (tablet move and async update from
KisAsyncronousStrokeUpdateHelper) now try to initiate a canvas update.
If enough time has passed and previous updates has finished, then a new
update is started.

One more important change: now actual layer offset change is executed in
the same exclusive job as the updates (with limited FPS), therefore, it
avoids garbage-looking leftovers of the intemediate move states.

BUG:410838
parent d9067261
......@@ -1737,6 +1737,11 @@ void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
m_d->scheduler.addSpontaneousJob(spontaneousJob);
}
bool KisImage::hasUpdatesRunning() const
{
return m_d->scheduler.hasUpdatesRunning();
}
void KisImage::setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter)
{
// update filters are *not* recursive!
......
......@@ -1074,6 +1074,14 @@ public Q_SLOTS:
*/
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
/**
* \return true if there are some updates in the updates queue
* Please note, that is doesn't guarantee that there are no updates
* running in in the updater context at the very moment. To guarantee that
* there are no updates left at all, please use barrier jobs instead.
*/
bool hasUpdatesRunning() const;
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks the current stroke should undo its last
......
......@@ -49,6 +49,8 @@ public:
virtual void disableUIUpdates() = 0;
virtual QVector<QRect> enableUIUpdates() = 0;
virtual bool hasUpdatesRunning() const = 0;
virtual void notifyBatchUpdateStarted() = 0;
virtual void notifyBatchUpdateEnded() = 0;
virtual void notifyUIUpdateCompleted(const QRect &rc) = 0;
......
......@@ -205,6 +205,11 @@ void KisUpdateScheduler::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
processQueues();
}
bool KisUpdateScheduler::hasUpdatesRunning() const
{
return !m_d->updatesQueue.isEmpty();
}
KisStrokeId KisUpdateScheduler::startStroke(KisStrokeStrategy *strokeStrategy)
{
KisStrokeId id = m_d->strokesQueue.startStroke(strokeStrategy);
......
......@@ -142,6 +142,8 @@ public:
void fullRefresh(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
bool hasUpdatesRunning() const;
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override;
void addJob(KisStrokeId id, KisStrokeJobData *data) override;
void endStroke(KisStrokeId id) override;
......
......@@ -189,6 +189,7 @@ set(kritaui_LIB_SRCS
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/KisAsyncronousStrokeUpdateHelper.cpp
tool/kis_tool_paint.cc
tool/kis_tool_shape.cc
tool/kis_tool_ellipse_base.cpp
......
......@@ -24,10 +24,12 @@
#include "stroke_testing_utils.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_resources_snapshot.h"
#include "kis_image.h"
#include <brushengine/kis_paint_information.h>
class FreehandStrokeBenchmarkTester : public utils::StrokeTester
{
public:
......@@ -86,7 +88,7 @@ protected:
image->addJob(strokeId(), data.take());
}
image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true));
image->addJob(strokeId(), new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
private:
......
......@@ -6,6 +6,7 @@
#include "stroke_testing_utils.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_resources_snapshot.h"
#include "kis_image.h"
#include <brushengine/kis_paint_information.h>
......@@ -85,7 +86,7 @@ protected:
image->addJob(strokeId(), data.take());
image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true));
image->addJob(strokeId(), new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
......
......@@ -24,6 +24,7 @@
#include "stroke_testing_utils.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_resources_snapshot.h"
#include "kis_image.h"
#include "kis_painter.h"
......@@ -124,7 +125,7 @@ protected:
new FreehandStrokeStrategy::Data(0, pi1, pi2));
image->addJob(strokeId(), data.take());
image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true));
image->addJob(strokeId(), new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
private:
......
/*
* Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_image_interfaces.h"
KisAsyncronousStrokeUpdateHelper::KisAsyncronousStrokeUpdateHelper()
: m_strokesFacade(0)
{
m_updateThresholdTimer.setSingleShot(false);
m_updateThresholdTimer.setInterval(80 /* ms */);
connect(&m_updateThresholdTimer, SIGNAL(timeout()), SLOT(slotAsyncUpdateCame()));
}
KisAsyncronousStrokeUpdateHelper::~KisAsyncronousStrokeUpdateHelper()
{
}
void KisAsyncronousStrokeUpdateHelper::startUpdateStream(KisStrokesFacade *strokesFacade, KisStrokeId strokeId)
{
m_strokesFacade = strokesFacade;
m_strokeId = strokeId;
m_updateThresholdTimer.start();
}
void KisAsyncronousStrokeUpdateHelper::endUpdateStream()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(isActive());
slotAsyncUpdateCame(true);
cancelUpdateStream();
}
void KisAsyncronousStrokeUpdateHelper::cancelUpdateStream()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(isActive());
m_updateThresholdTimer.stop();
m_strokeId.clear();
m_strokesFacade = 0;
}
bool KisAsyncronousStrokeUpdateHelper::isActive() const
{
return m_strokeId;
}
void KisAsyncronousStrokeUpdateHelper::setCustomUpdateDataFactory(KisAsyncronousStrokeUpdateHelper::UpdateDataFactory factory)
{
m_customUpdateFactory = factory;
}
void KisAsyncronousStrokeUpdateHelper::slotAsyncUpdateCame(bool forceUpdate)
{
if (!m_strokeId || !m_strokesFacade) return;
m_strokesFacade->addJob(m_strokeId,
m_customUpdateFactory ?
m_customUpdateFactory(forceUpdate) :
new UpdateData(forceUpdate));
}
/*
* Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KISASYNCRONOUSSTROKEUPDATEHELPER_H
#define KISASYNCRONOUSSTROKEUPDATEHELPER_H
#include <QObject>
#include <QTimer>
#include "kis_types.h"
#include "kis_stroke_job_strategy.h"
#include "kritaui_export.h"
class KisStrokesFacade;
class KRITAUI_EXPORT KisAsyncronousStrokeUpdateHelper : public QObject
{
Q_OBJECT
public:
class UpdateData : public KisStrokeJobData {
public:
UpdateData(bool _forceUpdate,
Sequentiality sequentiality = SEQUENTIAL,
Exclusivity exclusivity = NORMAL)
: KisStrokeJobData(sequentiality, exclusivity),
forceUpdate(_forceUpdate)
{}
KisStrokeJobData* createLodClone(int levelOfDetail) override {
return new UpdateData(*this, levelOfDetail);
}
private:
UpdateData(const UpdateData &rhs, int levelOfDetail)
: KisStrokeJobData(rhs),
forceUpdate(rhs.forceUpdate)
{
Q_UNUSED(levelOfDetail);
}
public:
bool forceUpdate = false;
};
using UpdateDataFactory = std::function<KisStrokeJobData*(bool)>;
public:
KisAsyncronousStrokeUpdateHelper();
~KisAsyncronousStrokeUpdateHelper();
void startUpdateStream(KisStrokesFacade *strokesFacade, KisStrokeId strokeId);
void endUpdateStream();
void cancelUpdateStream();
bool isActive() const;
void setCustomUpdateDataFactory(UpdateDataFactory factory);
private Q_SLOTS:
void slotAsyncUpdateCame(bool forceUpdate = false);
private:
KisStrokesFacade *m_strokesFacade;
QTimer m_updateThresholdTimer;
KisStrokeId m_strokeId;
UpdateDataFactory m_customUpdateFactory;
};
#endif // KISASYNCRONOUSSTROKEUPDATEHELPER_H
......@@ -25,6 +25,7 @@
#include "kis_image.h"
#include "kis_painter.h"
#include <strokes/KisFreehandStrokeInfo.h>
#include "KisAsyncronousStrokeUpdateHelper.h"
KisFigurePaintingToolHelper::KisFigurePaintingToolHelper(const KUndo2MagicString &name,
......@@ -55,7 +56,7 @@ KisFigurePaintingToolHelper::KisFigurePaintingToolHelper(const KUndo2MagicString
KisFigurePaintingToolHelper::~KisFigurePaintingToolHelper()
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::UpdateData(true));
new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
m_strokesFacade->endStroke(m_strokeId);
}
......
......@@ -44,6 +44,7 @@
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include <math.h>
......@@ -66,6 +67,7 @@ struct KisToolFreehandHelper::Private
{
KisPaintingInformationBuilder *infoBuilder;
KisStrokesFacade *strokesFacade;
KisAsyncronousStrokeUpdateHelper asyncUpdateHelper;
KUndo2MagicString transactionText;
......@@ -109,8 +111,6 @@ struct KisToolFreehandHelper::Private
KisStabilizedEventsSampler stabilizedSampler;
KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper;
QTimer asynchronousUpdatesThresholdTimer;
qreal effectiveSmoothnessDistance() const;
};
......@@ -131,7 +131,6 @@ KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *info
m_d->strokeTimeoutTimer.setSingleShot(true);
connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
connect(&m_d->asynchronousUpdatesThresholdTimer, SIGNAL(timeout()), SLOT(doAsynchronousUpdate()));
connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged()));
......@@ -313,8 +312,7 @@ void KisToolFreehandHelper::initPaintImpl(qreal startAngle,
m_d->airbrushingTimer.setInterval(computeAirbrushTimerInterval());
m_d->airbrushingTimer.start();
} else if (m_d->resources->presetNeedsAsynchronousUpdates()) {
m_d->asynchronousUpdatesThresholdTimer.setInterval(80 /* msec */);
m_d->asynchronousUpdatesThresholdTimer.start();
m_d->asyncUpdateHelper.startUpdateStream(m_d->strokesFacade, m_d->strokeId);
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
......@@ -615,14 +613,14 @@ void KisToolFreehandHelper::endPaint()
m_d->airbrushingTimer.stop();
}
if (m_d->asynchronousUpdatesThresholdTimer.isActive()) {
m_d->asynchronousUpdatesThresholdTimer.stop();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerEnd();
}
if (m_d->asyncUpdateHelper.isActive()) {
m_d->asyncUpdateHelper.endUpdateStream();
}
/**
* There might be some timer events still pending, so
* we should cancel them. Use this flag for the purpose.
......@@ -631,9 +629,6 @@ void KisToolFreehandHelper::endPaint()
*/
m_d->strokeInfos.clear();
// last update to complete rendering if there is still something pending
doAsynchronousUpdate(true);
m_d->strokesFacade->endStroke(m_d->strokeId);
m_d->strokeId.clear();
}
......@@ -648,8 +643,8 @@ void KisToolFreehandHelper::cancelPaint()
m_d->airbrushingTimer.stop();
}
if (m_d->asynchronousUpdatesThresholdTimer.isActive()) {
m_d->asynchronousUpdatesThresholdTimer.stop();
if (m_d->asyncUpdateHelper.isActive()) {
m_d->asyncUpdateHelper.cancelUpdateStream();
}
if (m_d->stabilizerPollTimer.isActive()) {
......@@ -881,12 +876,6 @@ void KisToolFreehandHelper::doAirbrushing()
}
}
void KisToolFreehandHelper::doAsynchronousUpdate(bool forceUpdate)
{
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::UpdateData(forceUpdate));
}
int KisToolFreehandHelper::computeAirbrushTimerInterval() const
{
qreal realInterval = m_d->resources->airbrushingInterval() * AIRBRUSH_INTERVAL_FACTOR;
......
......@@ -151,7 +151,6 @@ private:
private Q_SLOTS:
void finishStroke();
void doAirbrushing();
void doAsynchronousUpdate(bool forceUpdate = false);
void stabilizerPollAndPaint();
void slotSmoothingTypeChanged();
......
......@@ -39,7 +39,7 @@
#include <strokes/KisMaskedFreehandStrokePainter.h>
#include "brushengine/kis_paintop_utils.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
struct FreehandStrokeStrategy::Private
{
......@@ -145,7 +145,9 @@ void FreehandStrokeStrategy::finishStrokeCallback()
void FreehandStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
if (UpdateData *d = dynamic_cast<UpdateData*>(data)) {
if (KisAsyncronousStrokeUpdateHelper::UpdateData *d =
dynamic_cast<KisAsyncronousStrokeUpdateHelper::UpdateData*>(data)) {
// this job is lod-clonable in contrast to FreehandStrokeRunnableJobDataWithUpdate!
tryDoUpdate(d->forceUpdate);
......
......@@ -176,29 +176,6 @@ public:
KoColor customColor;
};
class UpdateData : public KisStrokeJobData {
public:
UpdateData(bool _forceUpdate)
: KisStrokeJobData(KisStrokeJobData::SEQUENTIAL),
forceUpdate(_forceUpdate)
{}
KisStrokeJobData* createLodClone(int levelOfDetail) override {
return new UpdateData(*this, levelOfDetail);
}
private:
UpdateData(const UpdateData &rhs, int levelOfDetail)
: KisStrokeJobData(rhs),
forceUpdate(rhs.forceUpdate)
{
Q_UNUSED(levelOfDetail);
}
public:
bool forceUpdate = false;
};
public:
FreehandStrokeStrategy(KisResourcesSnapshotSP resources,
KisFreehandStrokeInfo *strokeInfo,
......
......@@ -99,6 +99,7 @@ void MoveStrokeStrategy::initStrokeCallback()
KisStrokeStrategyUndoCommandBased::initStrokeCallback();
emit sigHandlesRectCalculated(handlesRect);
m_updateTimer.start();
}
void MoveStrokeStrategy::finishStrokeCallback()
......@@ -126,29 +127,50 @@ void MoveStrokeStrategy::finishStrokeCallback()
void MoveStrokeStrategy::cancelStrokeCallback()
{
if (!m_nodes.isEmpty()) {
// FIXME: make cancel job exclusive instead
m_updatesFacade->blockUpdates();
moveAndUpdate(QPoint());
m_updatesFacade->unblockUpdates();
m_finalOffset = QPoint();
m_hasPostponedJob = true;
tryPostUpdateJob(true);
}
KisStrokeStrategyUndoCommandBased::cancelStrokeCallback();
}
void MoveStrokeStrategy::tryPostUpdateJob(bool forceUpdate)
{
if (!m_hasPostponedJob) return;
if (forceUpdate ||
(m_updateTimer.elapsed() > m_updateInterval &&
!m_updatesFacade->hasUpdatesRunning())) {
addMutatedJob(new BarrierUpdateData(forceUpdate));
}
}
void MoveStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Data *d = dynamic_cast<Data*>(data);
if(!m_nodes.isEmpty() && d) {
moveAndUpdate(d->offset);
if (!m_nodes.isEmpty() && d) {
/**
* NOTE: we do not care about threading here, because
* all our jobs are declared sequential
*/
m_finalOffset = d->offset;
}
else {
m_hasPostponedJob = true;
tryPostUpdateJob(false);
} else if (BarrierUpdateData *barrierData =
dynamic_cast<BarrierUpdateData*>(data)) {
doCanvasUpdate(barrierData->forceUpdate);
} else if (KisAsyncronousStrokeUpdateHelper::UpdateData *updateData =
dynamic_cast<KisAsyncronousStrokeUpdateHelper::UpdateData*>(data)) {
tryPostUpdateJob(updateData->forceUpdate);
} else {
KisStrokeStrategyUndoCommandBased::doStrokeCallback(data);
}
}
......@@ -156,10 +178,19 @@ void MoveStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
#include "kis_selection_mask.h"
#include "kis_selection.h"
void MoveStrokeStrategy::moveAndUpdate(QPoint offset)
void MoveStrokeStrategy::doCanvasUpdate(bool forceUpdate)
{
if (!forceUpdate &&
(m_updateTimer.elapsed() < m_updateInterval ||
m_updatesFacade->hasUpdatesRunning())) {
return;
}
if (!m_hasPostponedJob) return;
Q_FOREACH (KisNodeSP node, m_nodes) {
QRect dirtyRect = moveNode(node, offset);
QRect dirtyRect = moveNode(node, m_finalOffset);
m_dirtyRects[node] |= dirtyRect;
if (m_updatesEnabled) {
......@@ -171,6 +202,9 @@ void MoveStrokeStrategy::moveAndUpdate(QPoint offset)
//mask->selection()->notifySelectionChanged();
}
}
m_hasPostponedJob = false;
m_updateTimer.restart();
}
QRect MoveStrokeStrategy::moveNode(KisNodeSP node, QPoint offset)
......
......@@ -26,6 +26,8 @@
#include "kis_stroke_strategy_undo_command_based.h"
#include "kis_types.h"
#include "kis_lod_transform.h"
#include <QElapsedTimer>
#include "KisAsyncronousStrokeUpdateHelper.h"
class KisUpdatesFacade;
......@@ -39,7 +41,7 @@ public:
class Data : public KisStrokeJobData {
public:
Data(QPoint _offset)
: KisStrokeJobData(SEQUENTIAL, EXCLUSIVE),
: KisStrokeJobData(SEQUENTIAL, NORMAL),
offset(_offset)
{
}
......@@ -59,6 +61,13 @@ public:
}
};
struct BarrierUpdateData : public KisAsyncronousStrokeUpdateHelper::UpdateData
{
BarrierUpdateData(bool forceUpdate)
: KisAsyncronousStrokeUpdateHelper::UpdateData(forceUpdate, BARRIER, EXCLUSIVE)
{}
};
public:
MoveStrokeStrategy(KisNodeList nodes, KisUpdatesFacade *updatesFacade,
KisStrokeUndoFacade *undoFacade);
......@@ -78,10 +87,11 @@ private:
void setUndoEnabled(bool value);
void setUpdatesEnabled(bool value);
private:
void moveAndUpdate(QPoint offset);
QRect moveNode(KisNodeSP node, QPoint offset);
void addMoveCommands(KisNodeSP node, KUndo2Command *parent);
void saveInitialNodeOffsets(KisNodeSP node);
void doCanvasUpdate(bool forceUpdate = false);
void tryPostUpdateJob(bool forceUpdate);
private:
KisNodeList m_nodes;
......@@ -92,6 +102,10 @@ private:
QHash<KisNodeSP, QRect> m_dirtyRects;
bool m_updatesEnabled;
QHash<KisNodeSP, QPoint> m_initialNodeOffsets;
QElapsedTimer m_updateTimer;
bool m_hasPostponedJob = false;
const int m_updateInterval = 30;
};
#endif /* __MOVE_STROKE_STRATEGY_H */
......@@ -23,6 +23,7 @@
#include "kis_paintop_settings.h"
#include <strokes/freehand_stroke.h>
#include <strokes/KisFreehandStrokeInfo.h>
#include "KisAsyncronousStrokeUpdateHelper.h"
#include <kis_brush.h>
KisPresetLivePreviewView::KisPresetLivePreviewView(QWidget *parent): QGraphicsView(parent)
......@@ -306,7 +307,7 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
QPointF(pointTwo.pos().x(),
handleY),
pointTwo));
m_image->addJob(strokeId, new FreehandStrokeStrategy::UpdateData(true));
m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
} else {
......@@ -330,7 +331,7 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
QPointF(m_canvasCenterPoint.x(),
m_canvasCenterPoint.y()+this->height()),
m_curvePointPI2));
m_image->addJob(strokeId, new FreehandStrokeStrategy::UpdateData(true));
m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
m_image->endStroke(strokeId);
m_image->waitForDone();
......
......@@ -174,6 +174,8 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
KisPaintLayerSP paintLayer = node ?
dynamic_cast<KisPaintLayer*>(node.data()) : 0;
bool isMoveSelection = false;
if (paintLayer && selection &&
(!selection->selectedRect().isEmpty() &&
!selection->selectedExactRect().isEmpty())) {
......@@ -189,6 +191,7 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
SLOT(slotHandlesRectCalculated(const QRect&)));
strategy = moveStrategy;
isMoveSelection = true;
} else {
......@@ -208,6 +211,10 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
m_currentlyProcessingNodes = nodes;
m_accumulatedOffset = QPoint();
if (!isMoveSelection) {
m_asyncUpdateHelper.startUpdateStream(image.data(), m_strokeId);
}
KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) {
m_changesTracker.reset();
}
......@@ -507,6 +514,10 @@ void KisToolMove::endStroke()
{
if (!m_strokeId) return;
if (m_asyncUpdateHelper.isActive()) {
m_asyncUpdateHelper.endUpdateStream();
}
KisImageSP image = currentImage();
image->endStroke(m_strokeId);
m_strokeId.clear();
......@@ -573,6 +584,10 @@ void KisToolMove::cancelStroke()
{
if (!m_strokeId) return;
if (m_asyncUpdateHelper.isActive()) {
m_asyncUpdateHelper.cancelUpdateStream();
}
KisImageSP image = currentImage();
image->cancelStroke(m_strokeId);
m_strokeId.clear();
......
......@@ -33,6 +33,7 @@
#include "KisToolChangesTracker.h"
#include "kis_signal_compressor.h"
#include "kis_signal_auto_connection.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_canvas2.h"
......@@ -175,6 +176,8 @@ private:
QPoint m_lastCursorPos;
KisSignalCompressor m_updateCursorCompressor;
KisSignalAutoConnectionsStore m_actionConnections;
KisAsyncronousStrokeUpdateHelper m_asyncUpdateHelper;
};
......
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