[calligra] krita: [FEATURE] Pressure sensitive line tool

Dmitry Kazakov dimula73 at gmail.com
Tue Jul 1 11:01:47 UTC 2014


Git commit 4a69efca51e9b728d3eb4d518ba18173c9f3bb08 by Dmitry Kazakov.
Committed on 01/07/2014 at 09:33.
Pushed by dkazakov into branch 'master'.

[FEATURE] Pressure sensitive line tool

Now when you draw the line tool, the preview is updated automatically,
more than that, you can control pressure and other sensors on the fly!

What's more, now you can use all the complicated sensors and mirroring
with the line tool!

BUG:333873
CCMAIL:kimageshop at kde.org

M  +4    -0    krita/image/kis_global.h
M  +18   -5    krita/image/kis_simple_stroke_strategy.cpp
M  +6    -1    krita/image/kis_simple_stroke_strategy.h
M  +1    -0    krita/plugins/tools/defaulttools/CMakeLists.txt
M  +56   -75   krita/plugins/tools/defaulttools/kis_tool_line.cc
M  +18   -10   krita/plugins/tools/defaulttools/kis_tool_line.h
A  +153  -0    krita/plugins/tools/defaulttools/kis_tool_line_helper.cpp     [License: GPL (v2+)]
A  +54   -0    krita/plugins/tools/defaulttools/kis_tool_line_helper.h     [License: GPL (v2+)]
M  +20   -7    krita/ui/tool/kis_painting_information_builder.cpp
M  +17   -3    krita/ui/tool/kis_painting_information_builder.h
M  +6    -2    krita/ui/tool/kis_tool.h
M  +1    -1    krita/ui/tool/kis_tool_freehand.cc
M  +1    -1    krita/ui/tool/kis_tool_freehand.h
M  +80   -12   krita/ui/tool/kis_tool_freehand_helper.cpp
M  +28   -0    krita/ui/tool/kis_tool_freehand_helper.h
M  +10   -5    krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp

http://commits.kde.org/calligra/4a69efca51e9b728d3eb4d518ba18173c9f3bb08

diff --git a/krita/image/kis_global.h b/krita/image/kis_global.h
index 01d895a..818ecb9 100644
--- a/krita/image/kis_global.h
+++ b/krita/image/kis_global.h
@@ -135,5 +135,9 @@ inline T kisGrowRect(const T &rect, U offset) {
     return rect.adjusted(-offset, -offset, offset, offset);
 }
 
+inline qreal kisDistance(const QPointF &pt1, const QPointF &pt2) {
+    return std::sqrt(pow2(pt1.x() - pt2.x()) + pow2(pt1.y() - pt2.y()));
+}
+
 #endif // KISGLOBAL_H_
 
diff --git a/krita/image/kis_simple_stroke_strategy.cpp b/krita/image/kis_simple_stroke_strategy.cpp
index cd64333..39318ba 100644
--- a/krita/image/kis_simple_stroke_strategy.cpp
+++ b/krita/image/kis_simple_stroke_strategy.cpp
@@ -65,13 +65,19 @@ private:
 
 KisSimpleStrokeStrategy::KisSimpleStrokeStrategy(QString id, const KUndo2MagicString &name)
     : KisStrokeStrategy(id, name),
-      m_jobEnabled(4, false)
+      m_jobEnabled(4, false),
+      m_jobSequentiality(4, KisStrokeJobData::SEQUENTIAL),
+      m_jobExclusivity(4, KisStrokeJobData::NORMAL)
 {
 }
 
-void KisSimpleStrokeStrategy::enableJob(JobType type, bool enable)
+void KisSimpleStrokeStrategy::enableJob(JobType type, bool enable,
+                                        KisStrokeJobData::Sequentiality sequentiality,
+                                        KisStrokeJobData::Exclusivity exclusivity)
 {
     m_jobEnabled[(int)type] = enable;
+    m_jobSequentiality[(int)type] = sequentiality;
+    m_jobExclusivity[(int)type] = exclusivity;
 }
 
 KisStrokeJobStrategy*
@@ -106,20 +112,27 @@ KisStrokeJobStrategy* KisSimpleStrokeStrategy::createDabStrategy()
     return createStrategy(JOB_DOSTROKE);
 }
 
+KisStrokeJobData* KisSimpleStrokeStrategy::createData(JobType type)
+{
+    KisStrokeJobData::Sequentiality sequentiality = m_jobSequentiality[(int)type];
+    KisStrokeJobData::Exclusivity exclusivity = m_jobExclusivity[(int)type];
+
+    return new KisStrokeJobData(sequentiality, exclusivity);
+}
 
 KisStrokeJobData* KisSimpleStrokeStrategy::createInitData()
 {
-    return 0;
+    return createData(JOB_INIT);
 }
 
 KisStrokeJobData* KisSimpleStrokeStrategy::createFinishData()
 {
-    return 0;
+    return createData(JOB_FINISH);
 }
 
 KisStrokeJobData* KisSimpleStrokeStrategy::createCancelData()
 {
-    return 0;
+    return createData(JOB_CANCEL);
 }
 
 void KisSimpleStrokeStrategy::initStrokeCallback()
diff --git a/krita/image/kis_simple_stroke_strategy.h b/krita/image/kis_simple_stroke_strategy.h
index fdf02fa..09e3ca6 100644
--- a/krita/image/kis_simple_stroke_strategy.h
+++ b/krita/image/kis_simple_stroke_strategy.h
@@ -51,13 +51,18 @@ public:
     virtual void doStrokeCallback(KisStrokeJobData *data);
 
 protected:
-    void enableJob(JobType type, bool enable = true);
+    void enableJob(JobType type, bool enable = true,
+                   KisStrokeJobData::Sequentiality sequentiality = KisStrokeJobData::SEQUENTIAL,
+                   KisStrokeJobData::Exclusivity exclusivity = KisStrokeJobData::NORMAL);
 
 private:
     KisStrokeJobStrategy* createStrategy(JobType type);
+    KisStrokeJobData* createData(JobType type);
 
 private:
     QVector<bool> m_jobEnabled;
+    QVector<KisStrokeJobData::Sequentiality> m_jobSequentiality;
+    QVector<KisStrokeJobData::Exclusivity> m_jobExclusivity;
 };
 
 #endif /* __KIS_SIMPLE_STROKE_STRATEGY_H */
diff --git a/krita/plugins/tools/defaulttools/CMakeLists.txt b/krita/plugins/tools/defaulttools/CMakeLists.txt
index 6a27edf..a33a1b1 100644
--- a/krita/plugins/tools/defaulttools/CMakeLists.txt
+++ b/krita/plugins/tools/defaulttools/CMakeLists.txt
@@ -7,6 +7,7 @@ set(kritadefaulttools_PART_SRCS
     kis_tool_colorpicker.cc
     kis_tool_brush.cc
     kis_tool_line.cc
+    kis_tool_line_helper.cpp
     kis_tool_fill.cc
     kis_tool_rectangle.cc
     kis_tool_ellipse.cc
diff --git a/krita/plugins/tools/defaulttools/kis_tool_line.cc b/krita/plugins/tools/defaulttools/kis_tool_line.cc
index 0fb274a..f776ba2 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_line.cc
+++ b/krita/plugins/tools/defaulttools/kis_tool_line.cc
@@ -42,17 +42,26 @@
 #include <recorder/kis_recorded_path_paint_action.h>
 #include <recorder/kis_node_query_path.h>
 
+#include "kis_painting_information_builder.h"
+#include "kis_tool_line_helper.h"
+
 #define ENABLE_RECORDING
 
 
 KisToolLine::KisToolLine(KoCanvasBase * canvas)
-        : KisToolPaint(canvas, KisCursor::load("tool_line_cursor.png", 6, 6))
+    : KisToolPaint(canvas, KisCursor::load("tool_line_cursor.png", 6, 6)),
+      m_infoBuilder(new KisToolPaintingInformationBuilder(this)),
+      m_helper(new KisToolLineHelper(m_infoBuilder.data(), kundo2_i18n("Draw Line"))),
+      m_strokeUpdateCompressor(500, KisSignalCompressor::FIRST_ACTIVE),
+      m_longStrokeUpdateCompressor(1000, KisSignalCompressor::FIRST_INACTIVE)
 {
     setObjectName("tool_line");
-    m_painter = 0;
     currentImage() = 0;
 
     setSupportOutline(true);
+
+    connect(&m_strokeUpdateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
+    connect(&m_longStrokeUpdateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
 }
 
 KisToolLine::~KisToolLine()
@@ -67,23 +76,10 @@ int KisToolLine::flags() const
 QWidget* KisToolLine::createOptionWidget()
 {
     QWidget* widget = KisToolPaint::createOptionWidget();
-    m_cbPressure     = new QCheckBox(i18n("Pressure"));
-    m_cbTilt         = new QCheckBox(i18n("Tilt"));
-    m_cbRotation     = new QCheckBox(i18n("Rotation"));
-    m_cbTangPressure = new QCheckBox(i18n("Tangential Pressure"));
-    m_bnVaryingEnds  = new QPushButton(i18n("Varying End-Points"));
-
-    m_cbPressure->setChecked(true);
-    m_cbTilt->setChecked(true);
-    m_cbRotation->setChecked(true);
-    m_cbTangPressure->setChecked(true);
-    m_bnVaryingEnds->setCheckable(true);
-
-    addOptionWidgetOption(m_cbPressure);
-    addOptionWidgetOption(m_cbTilt);
-    addOptionWidgetOption(m_cbRotation);
-    addOptionWidgetOption(m_cbTangPressure);
-    addOptionWidgetOption(m_bnVaryingEnds);
+    m_chkUseSensors = new QCheckBox(i18n("Use sensors"));
+    m_chkUseSensors->setChecked(true);
+    addOptionWidgetOption(m_chkUseSensors);
+
     return widget;
 }
 
@@ -99,24 +95,29 @@ void KisToolLine::paint(QPainter& gc, const KoViewConverter &converter)
 
 void KisToolLine::beginPrimaryAction(KoPointerEvent *event)
 {
-    if (nodePaintAbility() == NONE || !nodeEditable()) {
+    NodePaintAbility nodeAbility = nodePaintAbility();
+    if (nodeAbility == NONE || !nodeEditable()) {
         event->ignore();
         return;
     }
 
     setMode(KisTool::PAINT_MODE);
 
-    m_startPos = KisPaintInformation(
-        convertToPixelCoord(event),
-        PRESSURE_DEFAULT,
-        m_cbTilt->isChecked() ? event->xTilt() : 0.0,
-        m_cbTilt->isChecked() ? event->yTilt() : 0.0,
-        m_cbRotation->isChecked() ? event->rotation() : 0.0,
-        m_cbTangPressure->isChecked() ? event->tangentialPressure() : 0.0
-        );
-
-    m_endPos      = m_startPos;
-    m_maxPressure = 0.0f;
+    m_helper->setEnabled(nodeAbility == PAINT);
+    m_helper->setUseSensors(m_chkUseSensors->isChecked());
+    m_helper->start(event);
+
+    m_startPoint = convertToPixelCoord(event);
+    m_endPoint = m_startPoint;
+    m_lastUpdatedPoint = m_startPoint;
+}
+
+void KisToolLine::updateStroke()
+{
+    m_helper->repaintLine(canvas()->resourceManager(),
+                          image(),
+                          image().data(),
+                          image()->postExecutionUndoAdapter());
 }
 
 void KisToolLine::continuePrimaryAction(KoPointerEvent *event)
@@ -129,66 +130,46 @@ void KisToolLine::continuePrimaryAction(KoPointerEvent *event)
     QPointF pos = convertToPixelCoord(event);
 
     if (event->modifiers() == Qt::AltModifier) {
-        QPointF trans = pos - m_endPos.pos();
-        m_startPos.setPos(m_startPos.pos() + trans);
-        m_endPos.setPos(m_endPos.pos() + trans);
+        QPointF trans = pos - m_endPoint;
+        m_helper->translatePoints(trans);
     } else if (event->modifiers() == Qt::ShiftModifier) {
-        m_endPos.setPos(straightLine(pos));
+        m_helper->addPoint(event, straightLine(pos));
     } else {
-        m_endPos.setPos(pos);
+        m_helper->addPoint(event);
     }
 
-    m_maxPressure = qMax(m_maxPressure, float(pressureToCurve(event->pressure())));
+    if ((pixelToView(m_lastUpdatedPoint) - pixelToView(pos)).manhattanLength() > 10) {
+        m_longStrokeUpdateCompressor.stop();
+        m_strokeUpdateCompressor.start();
+        m_lastUpdatedPoint = pos;
+    } else {
+        m_longStrokeUpdateCompressor.start();
+    }
+    m_endPoint = pos;
+
     updatePreview();
     KisToolPaint::requestUpdateOutline(event->point, event);
 }
 
 void KisToolLine::endPrimaryAction(KoPointerEvent *event)
 {
+    Q_UNUSED(event);
     CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
     setMode(KisTool::HOVER_MODE);
 
     updatePreview();
 
-    if(m_bnVaryingEnds->isChecked()) {
-        m_endPos = KisPaintInformation(
-            m_endPos.pos(),
-            PRESSURE_DEFAULT,
-            m_cbTilt->isChecked() ? event->xTilt() : 0.0,
-            m_cbTilt->isChecked() ? event->yTilt() : 0.0,
-            m_cbRotation->isChecked() ? event->rotation() : 0.0,
-            m_cbTangPressure->isChecked() ? event->tangentialPressure() : 0.0
-            );
-    }
-
-    if (m_startPos.pos() == m_endPos.pos())
+    if (m_startPoint == m_endPoint)
         return;
 
-    if(m_cbPressure->isChecked()) {
-        m_startPos.setPressure(m_maxPressure);
-        m_endPos.setPressure(m_maxPressure);
-    }
-
     NodePaintAbility nodeAbility = nodePaintAbility();
     if (nodeAbility == NONE) {
         return;
     }
-#ifdef ENABLE_RECORDING
-    if (image()) {
-        KisRecordedPathPaintAction linePaintAction(KisNodeQueryPath::absolutePath(currentNode()), currentPaintOpPreset());
-        setupPaintAction(&linePaintAction);
-        linePaintAction.addLine(m_startPos, m_endPos);
-        image()->actionRecorder()->addAction(linePaintAction);
-    }
-#endif
 
     if (nodeAbility == PAINT) {
-        KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Line"),
-                                           image(),
-                                           canvas()->resourceManager(),
-                                           KisPainter::StrokeStyleBrush,
-                                           KisPainter::FillStyleNone);
-        helper.paintLine(m_startPos, m_endPos);
+        updateStroke();
+        m_helper->end();
     }
     else {
         KoPathShape* path = new KoPathShape();
@@ -196,8 +177,8 @@ void KisToolLine::endPrimaryAction(KoPointerEvent *event)
 
         QTransform resolutionMatrix;
         resolutionMatrix.scale(1 / currentImage()->xRes(), 1 / currentImage()->yRes());
-        path->moveTo(resolutionMatrix.map(m_startPos.pos()));
-        path->lineTo(resolutionMatrix.map(m_endPos.pos()));
+        path->moveTo(resolutionMatrix.map(m_startPoint));
+        path->lineTo(resolutionMatrix.map(m_endPoint));
         path->normalize();
 
         KoShapeStroke* border = new KoShapeStroke(1.0, currentFgColor().toQColor());
@@ -210,7 +191,7 @@ void KisToolLine::endPrimaryAction(KoPointerEvent *event)
 
 QPointF KisToolLine::straightLine(QPointF point)
 {
-    const QPointF lineVector = point - m_startPos.pos();
+    const QPointF lineVector = point - m_startPoint;
     qreal lineAngle = std::atan2(lineVector.y(), lineVector.x());
 
     if (lineAngle < 0) {
@@ -226,7 +207,7 @@ QPointF KisToolLine::straightLine(QPointF point)
 
     const QPointF constrainedLineVector(lineLength * std::cos(constrainedLineAngle), lineLength * std::sin(constrainedLineAngle));
 
-    const QPointF result = m_startPos.pos() + constrainedLineVector;
+    const QPointF result = m_startPoint + constrainedLineVector;
 
     return result;
 }
@@ -235,7 +216,7 @@ QPointF KisToolLine::straightLine(QPointF point)
 void KisToolLine::updatePreview()
 {
     if (canvas()) {
-        QRectF bound(m_startPos.pos(), m_endPos.pos());
+        QRectF bound(m_startPoint, m_endPoint);
         canvas()->updateCanvas(convertToPt(bound.normalized().adjusted(-3, -3, 3, 3)));
     }
 }
@@ -243,8 +224,8 @@ void KisToolLine::updatePreview()
 
 void KisToolLine::paintLine(QPainter& gc, const QRect&)
 {
-    QPointF viewStartPos = pixelToView(m_startPos.pos());
-    QPointF viewStartEnd = pixelToView(m_endPos.pos());
+    QPointF viewStartPos = pixelToView(m_startPoint);
+    QPointF viewStartEnd = pixelToView(m_endPoint);
 
     if (canvas()) {
         QPainterPath path;
diff --git a/krita/plugins/tools/defaulttools/kis_tool_line.h b/krita/plugins/tools/defaulttools/kis_tool_line.h
index cceb585..58c90ab 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_line.h
+++ b/krita/plugins/tools/defaulttools/kis_tool_line.h
@@ -25,11 +25,12 @@
 
 #include "kis_tool_paint.h"
 
+#include <QScopedPointer>
 #include "kis_global.h"
 #include "kis_types.h"
 #include "KoToolFactoryBase.h"
 #include "flake/kis_node_shape.h"
-#include "kis_paint_information.h"
+#include "kis_signal_compressor.h"
 #include <KoIcon.h>
 
 class KisPainter;
@@ -38,6 +39,9 @@ class KoCanvasBase;
 class KisRecordedPolyLinePaintAction;
 class QCheckBox;
 class QPushButton;
+class KisToolPaintingInformationBuilder;
+class KisToolLineHelper;
+
 
 class KisToolLine : public KisToolPaint
 {
@@ -56,6 +60,9 @@ public:
 
     virtual QString quickHelp() const;
 
+private slots:
+    void updateStroke();
+
 private:
     void paintLine(QPainter& gc, const QRect& rc);
     QPointF straightLine(QPointF point);
@@ -63,15 +70,16 @@ private:
     virtual QWidget* createOptionWidget();
 
 private:
-    KisPaintInformation m_startPos;
-    KisPaintInformation m_endPos;
-    KisPainter*         m_painter;
-    QCheckBox*          m_cbPressure;
-    QCheckBox*          m_cbTilt;
-    QCheckBox*          m_cbRotation;
-    QCheckBox*          m_cbTangPressure;
-    QPushButton*        m_bnVaryingEnds;
-    float               m_maxPressure;
+    QPointF m_startPoint;
+    QPointF m_endPoint;
+    QPointF m_lastUpdatedPoint;
+
+    QCheckBox *m_chkUseSensors;
+
+    QScopedPointer<KisToolPaintingInformationBuilder> m_infoBuilder;
+    QScopedPointer<KisToolLineHelper> m_helper;
+    KisSignalCompressor m_strokeUpdateCompressor;
+    KisSignalCompressor m_longStrokeUpdateCompressor;
 };
 
 
diff --git a/krita/plugins/tools/defaulttools/kis_tool_line_helper.cpp b/krita/plugins/tools/defaulttools/kis_tool_line_helper.cpp
new file mode 100644
index 0000000..b19216b
--- /dev/null
+++ b/krita/plugins/tools/defaulttools/kis_tool_line_helper.cpp
@@ -0,0 +1,153 @@
+/*
+ *  Copyright (c) 2014 Dmitry Kazakov <dimula73 at 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 "kis_tool_line_helper.h"
+
+#include "kis_painting_information_builder.h"
+
+struct KisToolLineHelper::Private
+{
+    Private(KisPaintingInformationBuilder *_infoBuilder)
+        : infoBuilder(_infoBuilder),
+          useSensors(true),
+          enabled(true)
+    {
+    }
+
+    QVector<KisPaintInformation> linePoints;
+    KisPaintingInformationBuilder *infoBuilder;
+    bool useSensors;
+    bool enabled;
+};
+
+KisToolLineHelper::KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder,
+                                     const KUndo2MagicString &transactionText,
+                                     KisRecordingAdapter *recordingAdapter)
+    : KisToolFreehandHelper(infoBuilder, transactionText, recordingAdapter),
+      m_d(new Private(infoBuilder))
+{
+}
+
+KisToolLineHelper::~KisToolLineHelper()
+{
+}
+
+void KisToolLineHelper::setEnabled(bool value)
+{
+    m_d->enabled = value;
+}
+
+void KisToolLineHelper::setUseSensors(bool value)
+{
+    m_d->useSensors = value;
+}
+
+void KisToolLineHelper::repaintLine(KoCanvasResourceManager *resourceManager,
+                                    KisImageWSP image,
+                                    KisStrokesFacade *strokesFacade,
+                                    KisPostExecutionUndoAdapter *undoAdapter)
+{
+    if (!m_d->enabled) return;
+
+    cancelPaint();
+    if (m_d->linePoints.isEmpty()) return;
+
+    QVector<KisPaintInformation>::const_iterator it = m_d->linePoints.constBegin();
+    QVector<KisPaintInformation>::const_iterator end = m_d->linePoints.constEnd();
+
+    initPaintImpl(*it, resourceManager, image, strokesFacade, undoAdapter);
+    ++it;
+
+    while (it != end) {
+        paintLine(*(it - 1), *it);
+        ++it;
+    }
+}
+
+void KisToolLineHelper::start(KoPointerEvent *event)
+{
+    if (!m_d->enabled) return;
+
+    KisPaintInformation pi =
+            m_d->infoBuilder->startStroke(event, elapsedStrokeTime());
+
+    if (!m_d->useSensors) {
+        pi = KisPaintInformation(pi.pos());
+    }
+
+    m_d->linePoints.append(pi);
+}
+
+void KisToolLineHelper::addPoint(KoPointerEvent *event, const QPointF &overridePos)
+{
+    if (!m_d->enabled) return;
+
+    KisPaintInformation pi =
+            m_d->infoBuilder->continueStroke(event, elapsedStrokeTime());
+
+    if (!m_d->useSensors) {
+        pi = KisPaintInformation(pi.pos());
+    }
+
+    if (!overridePos.isNull()) {
+        pi.setPos(overridePos);
+    }
+
+    if (m_d->linePoints.size() > 1) {
+        const QPointF startPos = m_d->linePoints.first().pos();
+        const QPointF endPos = pi.pos();
+        const qreal maxDistance = kisDistance(startPos, endPos);
+        const QPointF unit = (endPos - startPos) / maxDistance;
+
+        QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin();
+        ++it;
+        while (it != m_d->linePoints.end()) {
+            qreal dist = kisDistance(startPos, it->pos());
+            if (dist < maxDistance) {
+                QPointF pos = startPos + unit * dist;
+                it->setPos(pos);
+                ++it;
+            } else {
+                it = m_d->linePoints.erase(it);
+            }
+        }
+    }
+
+    m_d->linePoints.append(pi);
+}
+
+void KisToolLineHelper::translatePoints(const QPointF &offset)
+{
+    if (!m_d->enabled) return;
+
+    QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin();
+    while (it != m_d->linePoints.end()) {
+        it->setPos(it->pos() + offset);
+        ++it;
+    }
+}
+
+void KisToolLineHelper::end()
+{
+    if (!m_d->enabled) return;
+
+    endPaint();
+    m_d->linePoints.clear();
+}
+
+
diff --git a/krita/plugins/tools/defaulttools/kis_tool_line_helper.h b/krita/plugins/tools/defaulttools/kis_tool_line_helper.h
new file mode 100644
index 0000000..57970fd
--- /dev/null
+++ b/krita/plugins/tools/defaulttools/kis_tool_line_helper.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2014 Dmitry Kazakov <dimula73 at 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 __KIS_TOOL_LINE_HELPER_H
+#define __KIS_TOOL_LINE_HELPER_H
+
+#include "kis_tool_freehand_helper.h"
+
+
+class KisToolLineHelper : private KisToolFreehandHelper
+{
+public:
+    KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder,
+                      const KUndo2MagicString &transactionText,
+                      KisRecordingAdapter *recordingAdapter = 0);
+
+    ~KisToolLineHelper();
+
+    void setEnabled(bool value);
+    void setUseSensors(bool value);
+
+    void repaintLine(KoCanvasResourceManager *resourceManager,
+                     KisImageWSP image,
+                     KisStrokesFacade *strokesFacade,
+                     KisPostExecutionUndoAdapter *undoAdapter);
+
+    void start(KoPointerEvent *event);
+    void addPoint(KoPointerEvent *event, const QPointF &overridePos = QPointF());
+    void translatePoints(const QPointF &offset);
+    void end();
+
+
+
+private:
+    struct Private;
+    Private * const m_d;
+};
+
+#endif /* __KIS_TOOL_LINE_HELPER_H */
diff --git a/krita/ui/tool/kis_painting_information_builder.cpp b/krita/ui/tool/kis_painting_information_builder.cpp
index 0eb52f1..6e3d19a 100644
--- a/krita/ui/tool/kis_painting_information_builder.cpp
+++ b/krita/ui/tool/kis_painting_information_builder.cpp
@@ -125,24 +125,37 @@ qreal KisPaintingInformationBuilder::pressureToCurve(qreal pressure)
 /*           KisToolPaintingInformationBuilder                        */
 /***********************************************************************/
 
-#include "kis_tool_freehand.h"
+#include "kis_tool.h"
 
-KisToolPaintingInformationBuilder::KisToolPaintingInformationBuilder(KisToolFreehand *tool)
+KisToolPaintingInformationBuilder::KisToolPaintingInformationBuilder(KisTool *tool)
     : m_tool(tool)
 {
 }
 
-QPointF KisToolPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &startPoint)
+QPointF KisToolPaintingInformationBuilder::documentToImage(const QPointF &point)
 {
-    return m_tool->adjustPosition(point, startPoint);
+    return m_tool->convertToPixelCoord(point);
 }
 
-QPointF KisToolPaintingInformationBuilder::documentToImage(const QPointF &point)
+
+/***********************************************************************/
+/*           KisToolFreehandPaintingInformationBuilder                        */
+/***********************************************************************/
+
+#include "kis_tool_freehand.h"
+
+KisToolFreehandPaintingInformationBuilder::KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool)
+    : KisToolPaintingInformationBuilder(tool),
+      m_tool(tool)
 {
-    return m_tool->convertToPixelCoord(point);
 }
 
-qreal KisToolPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
+QPointF KisToolFreehandPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &startPoint)
+{
+    return m_tool->adjustPosition(point, startPoint);
+}
+
+qreal KisToolFreehandPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
 {
     return m_tool->calculatePerspective(documentPoint);
 }
diff --git a/krita/ui/tool/kis_painting_information_builder.h b/krita/ui/tool/kis_painting_information_builder.h
index 7ff5c80..993c25b 100644
--- a/krita/ui/tool/kis_painting_information_builder.h
+++ b/krita/ui/tool/kis_painting_information_builder.h
@@ -27,6 +27,7 @@
 #include "kis_paint_information.h"
 
 class KoPointerEvent;
+class KisTool;
 class KisToolFreehand;
 
 
@@ -70,17 +71,30 @@ private:
     QPointF m_startPoint;
 };
 
-
 class KRITAUI_EXPORT KisToolPaintingInformationBuilder : public KisPaintingInformationBuilder
 {
     Q_OBJECT
 
 public:
-    KisToolPaintingInformationBuilder(KisToolFreehand *tool);
+    KisToolPaintingInformationBuilder(KisTool *tool);
 
 protected:
-    virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint);
     virtual QPointF documentToImage(const QPointF &point);
+
+private:
+    KisTool *m_tool;
+};
+
+
+class KRITAUI_EXPORT KisToolFreehandPaintingInformationBuilder : public KisToolPaintingInformationBuilder
+{
+    Q_OBJECT
+
+public:
+    KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool);
+
+protected:
+    virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint);
     virtual qreal calculatePerspective(const QPointF &documentPoint);
 
 private:
diff --git a/krita/ui/tool/kis_tool.h b/krita/ui/tool/kis_tool.h
index 558fdc3..6f7deca 100644
--- a/krita/ui/tool/kis_tool.h
+++ b/krita/ui/tool/kis_tool.h
@@ -208,14 +208,18 @@ public slots:
     virtual void canvasResourceChanged(int key, const QVariant & res);
 
 protected:
-    QPointF widgetCenterInWidgetPixels();
-    QPointF convertDocumentToWidget(const QPointF& pt);
+    // conversion methods are also needed by the paint information builder
+    friend class KisToolPaintingInformationBuilder;
 
     /// Convert from native (postscript points) to image pixel
     /// coordinates.
     QPointF convertToPixelCoord(KoPointerEvent *e);
     QPointF convertToPixelCoord(const QPointF& pt);
 
+protected:
+    QPointF widgetCenterInWidgetPixels();
+    QPointF convertDocumentToWidget(const QPointF& pt);
+
     /// Convert from native (postscript points) to integer image pixel
     /// coordinates. This truncates the floating point components and
     /// should be used in preference to QPointF::toPoint(), which rounds,
diff --git a/krita/ui/tool/kis_tool_freehand.cc b/krita/ui/tool/kis_tool_freehand.cc
index efc321e..df9512e 100644
--- a/krita/ui/tool/kis_tool_freehand.cc
+++ b/krita/ui/tool/kis_tool_freehand.cc
@@ -68,7 +68,7 @@ KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, const QCursor & cursor,
 
     setSupportOutline(true);
 
-    m_infoBuilder = new KisToolPaintingInformationBuilder(this);
+    m_infoBuilder = new KisToolFreehandPaintingInformationBuilder(this);
     m_recordingAdapter = new KisRecordingAdapter();
     m_helper = new KisToolFreehandHelper(m_infoBuilder, transactionText, m_recordingAdapter);
 
diff --git a/krita/ui/tool/kis_tool_freehand.h b/krita/ui/tool/kis_tool_freehand.h
index 56cb836..66e7d6c 100644
--- a/krita/ui/tool/kis_tool_freehand.h
+++ b/krita/ui/tool/kis_tool_freehand.h
@@ -93,7 +93,7 @@ protected slots:
     void setAssistant(bool assistant);
 
 private:
-    friend class KisToolPaintingInformationBuilder;
+    friend class KisToolFreehandPaintingInformationBuilder;
 
     /**
      * Adjusts a coordinates according to a KisPaintingAssitant,
diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp b/krita/ui/tool/kis_tool_freehand_helper.cpp
index 80decb3..5ea0866 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.cpp
+++ b/krita/ui/tool/kis_tool_freehand_helper.cpp
@@ -198,6 +198,26 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
                                       KisNodeSP overrideNode,
                                       KisDefaultBoundsBaseSP bounds)
 {
+    KisPaintInformation pi =
+        m_d->infoBuilder->startStroke(event, elapsedStrokeTime());
+
+    initPaintImpl(pi,
+                  resourceManager,
+                  image,
+                  strokesFacade,
+                  undoAdapter,
+                  overrideNode,
+                  bounds);
+}
+
+void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation,
+                                          KoCanvasResourceManager *resourceManager,
+                                          KisImageWSP image,
+                                          KisStrokesFacade *strokesFacade,
+                                          KisPostExecutionUndoAdapter *undoAdapter,
+                                          KisNodeSP overrideNode,
+                                          KisDefaultBoundsBaseSP bounds)
+{
     Q_UNUSED(overrideNode);
 
     m_d->strokesFacade = strokesFacade;
@@ -209,8 +229,7 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
 
     m_d->strokeTime.start();
 
-    m_d->previousPaintInformation =
-            m_d->infoBuilder->startStroke(event, m_d->strokeTime.elapsed());
+    m_d->previousPaintInformation = previousPaintInformation;
 
     createPainters(m_d->painterInfos,
                    m_d->previousPaintInformation.pos(),
@@ -333,8 +352,7 @@ void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaint
         control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
     }
 
-    paintBezierCurve(m_d->painterInfos,
-                     pi1,
+    paintBezierCurve(pi1,
                      control1,
                      control2,
                      pi2);
@@ -344,7 +362,7 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
 {
     KisPaintInformation info =
             m_d->infoBuilder->continueStroke(event,
-                                             m_d->strokeTime.elapsed());
+                                             elapsedStrokeTime());
 
     /**
      * Smooth the coordinates out using the history and the
@@ -485,7 +503,7 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
         m_d->strokeTimeoutTimer.start(100);
     }
     else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
-        paintLine(m_d->painterInfos, m_d->previousPaintInformation, info);
+        paintLine(m_d->previousPaintInformation, info);
     }
 
     if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
@@ -502,7 +520,7 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
 void KisToolFreehandHelper::endPaint()
 {
     if (!m_d->hasPaintAtLeastOnce) {
-        paintAt(m_d->painterInfos, m_d->previousPaintInformation);
+        paintAt(m_d->previousPaintInformation);
     } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
         finishStroke();
     }
@@ -525,12 +543,44 @@ void KisToolFreehandHelper::endPaint()
     m_d->painterInfos.clear();
 
     m_d->strokesFacade->endStroke(m_d->strokeId);
+    m_d->strokeId.clear();
 
     if(m_d->recordingAdapter) {
         m_d->recordingAdapter->endStroke();
     }
 }
 
+void KisToolFreehandHelper::cancelPaint()
+{
+    if (!m_d->strokeId) return;
+
+    m_d->strokeTimeoutTimer.stop();
+
+    if (m_d->airbrushingTimer.isActive()) {
+        m_d->airbrushingTimer.stop();
+    }
+
+    if (m_d->stabilizerPollTimer.isActive()) {
+        m_d->stabilizerPollTimer.stop();
+    }
+
+    // see a comment in endPaint()
+    m_d->painterInfos.clear();
+
+    m_d->strokesFacade->cancelStroke(m_d->strokeId);
+    m_d->strokeId.clear();
+
+    if(m_d->recordingAdapter) {
+        //FIXME: not implemented
+        //m_d->recordingAdapter->cancelStroke();
+    }
+}
+
+int KisToolFreehandHelper::elapsedStrokeTime() const
+{
+    return m_d->strokeTime.elapsed();
+}
+
 void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
 {
     // FIXME: Ugly hack, this is no a "distance" in any way
@@ -595,7 +645,7 @@ void KisToolFreehandHelper::stabilizerPollAndPaint()
     }
 
     if (canPaint) {
-        paintLine(m_d->painterInfos, m_d->previousPaintInformation, newInfo);
+        paintLine(m_d->previousPaintInformation, newInfo);
         m_d->previousPaintInformation = newInfo;
 
         // Push the new entry through the queue
@@ -659,7 +709,7 @@ void KisToolFreehandHelper::finishStroke()
 void KisToolFreehandHelper::doAirbrushing()
 {
     if(!m_d->painterInfos.isEmpty()) {
-        paintAt(m_d->painterInfos, m_d->previousPaintInformation);
+        paintAt(m_d->previousPaintInformation);
     }
 }
 
@@ -706,18 +756,18 @@ void KisToolFreehandHelper::paintBezierCurve(PainterInfo *painterInfo,
     tpi1.setPressure(0.3);
     tpi2.setPressure(0.3);
 
-    paintLine(m_d->painterInfos, tpi1, tpi2);
+    paintLine(tpi1, tpi2);
 
     tpi1.setPressure(0.6);
     tpi2.setPressure(0.3);
 
     tpi1.setPos(pi1.pos());
     tpi2.setPos(control1);
-    paintLine(m_d->painterInfos, tpi1, tpi2);
+    paintLine(tpi1, tpi2);
 
     tpi1.setPos(pi2.pos());
     tpi2.setPos(control2);
-    paintLine(m_d->painterInfos, tpi1, tpi2);
+    paintLine(tpi1, tpi2);
 #endif
 
     m_d->hasPaintAtLeastOnce = true;
@@ -762,3 +812,21 @@ void KisToolFreehandHelper::paintBezierCurve(const QVector<PainterInfo*> &painte
     paintBezierCurve(painterInfos.first(), pi1, control1, control2, pi2);
 }
 
+void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
+{
+    paintAt(m_d->painterInfos, pi);
+}
+
+void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
+                                      const KisPaintInformation &pi2)
+{
+    paintLine(m_d->painterInfos, pi1, pi2);
+}
+
+void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
+                                             const QPointF &control1,
+                                             const QPointF &control2,
+                                             const KisPaintInformation &pi2)
+{
+    paintBezierCurve(m_d->painterInfos, pi1, control1, control2, pi2);
+}
diff --git a/krita/ui/tool/kis_tool_freehand_helper.h b/krita/ui/tool/kis_tool_freehand_helper.h
index a350514..3ab5701 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.h
+++ b/krita/ui/tool/kis_tool_freehand_helper.h
@@ -83,11 +83,26 @@ signals:
     void requestExplicitUpdateOutline();
 
 protected:
+    void cancelPaint();
+    int elapsedStrokeTime() const;
+
+    void initPaintImpl(const KisPaintInformation &previousPaintInformation,
+                       KoCanvasResourceManager *resourceManager,
+                       KisImageWSP image,
+                       KisStrokesFacade *strokesFacade,
+                       KisPostExecutionUndoAdapter *undoAdapter,
+                       KisNodeSP overrideNode = 0,
+                       KisDefaultBoundsBaseSP bounds = 0);
+
+protected:
 
     virtual void createPainters(QVector<PainterInfo*> &painterInfos,
                                 const QPointF &lastPosition,
                                 int lastTime);
 
+    // to be overridden in derived classes to add painting with
+    // multiple painters
+
     virtual void paintAt(const QVector<PainterInfo*> &painterInfos,
                          const KisPaintInformation &pi);
 
@@ -101,6 +116,7 @@ protected:
                                   const QPointF &control2,
                                   const KisPaintInformation &pi2);
 
+    // lo-level methods for painting primitives
 
     void paintAt(PainterInfo *painterInfo, const KisPaintInformation &pi);
 
@@ -114,6 +130,18 @@ protected:
                           const QPointF &control2,
                           const KisPaintInformation &pi2);
 
+    // hi-level methods for painting primitives
+
+    void paintAt(const KisPaintInformation &pi);
+
+    void paintLine(const KisPaintInformation &pi1,
+                   const KisPaintInformation &pi2);
+
+    void paintBezierCurve(const KisPaintInformation &pi1,
+                          const QPointF &control1,
+                          const QPointF &control2,
+                          const KisPaintInformation &pi2);
+
 private:
     void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
                                                    QPointF tangent1, QPointF tangent2);
diff --git a/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp b/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
index 76de587..ff09dab 100644
--- a/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
+++ b/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
@@ -68,7 +68,7 @@ void KisPainterBasedStrokeStrategy::init()
 {
     enableJob(KisSimpleStrokeStrategy::JOB_INIT);
     enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
-    enableJob(KisSimpleStrokeStrategy::JOB_CANCEL);
+    enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
 }
 
 KisPaintDeviceSP KisPainterBasedStrokeStrategy::targetDevice()
@@ -178,15 +178,20 @@ void KisPainterBasedStrokeStrategy::finishStrokeCallback()
 
 void KisPainterBasedStrokeStrategy::cancelStrokeCallback()
 {
-    m_transaction->revert();
-    delete m_transaction;
-    deletePainters();
-
     KisNodeSP node = m_resources->currentNode();
     KisIndirectPaintingSupport *indirect =
         dynamic_cast<KisIndirectPaintingSupport*>(node.data());
 
     if(indirect && indirect->hasTemporaryTarget()) {
+        delete m_transaction;
+        deletePainters();
+
+        QRegion region = indirect->temporaryTarget()->region();
         indirect->setTemporaryTarget(0);
+        node->setDirty(region);
+    } else {
+        m_transaction->revert();
+        delete m_transaction;
+        deletePainters();
     }
 }



More information about the kimageshop mailing list