Git commit f9cbf6a1bd864be88059255aa9c4b5b35208085d by Boudewijn Rempt. Committed on 31/12/2012 at 17:04. Pushed by rempt into branch 'master'. Implement line smoothing in Krita REVIEW:108049 BUG:281267 M +1 -1 krita/image/brushengine/kis_paint_information.h M +17 -3 krita/image/kis_distance_information.h M +5 -3 krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor= s.cc M +54 -18 krita/plugins/tools/defaulttools/kis_tool_brush.cc M +8 -3 krita/plugins/tools/defaulttools/kis_tool_brush.h M +1 -0 krita/ui/CMakeLists.txt C +8 -17 krita/ui/tool/kis_smoothing_options.cpp [from: krita/image/k= is_distance_information.h - 057% similarity] C +20 -14 krita/ui/tool/kis_smoothing_options.h [from: krita/image/kis= _distance_information.h - 057% similarity] M +1 -8 krita/ui/tool/kis_tool_freehand.cc M +3 -3 krita/ui/tool/kis_tool_freehand.h M +111 -31 krita/ui/tool/kis_tool_freehand_helper.cpp M +7 -3 krita/ui/tool/kis_tool_freehand_helper.h http://commits.kde.org/calligra/f9cbf6a1bd864be88059255aa9c4b5b35208085d diff --git a/krita/image/brushengine/kis_paint_information.h b/krita/image/= brushengine/kis_paint_information.h index 0142a33..8e1de78 100644 --- a/krita/image/brushengine/kis_paint_information.h +++ b/krita/image/brushengine/kis_paint_information.h @@ -111,7 +111,7 @@ public: = /// Number of ms since the beginning of the stroke int currentTime() const; - = + void toXML(QDomDocument&, QDomElement&) const; = static KisPaintInformation fromXML(const QDomElement&); diff --git a/krita/image/kis_distance_information.h b/krita/image/kis_dista= nce_information.h index c1ab409..f2bd080 100644 --- a/krita/image/kis_distance_information.h +++ b/krita/image/kis_distance_information.h @@ -24,9 +24,23 @@ * to be passed for the next call. */ struct KisDistanceInformation { - KisDistanceInformation() : distance(0), spacing(0) {} - KisDistanceInformation(double _distance, double _spacing) : distance(_= distance), spacing(_spacing) {} - void clear() { distance =3D 0; spacing =3D 0;} + + KisDistanceInformation() + : distance(0) + , spacing(0) + {} + + KisDistanceInformation(double _distance, double _spacing) + : distance(_distance) + , spacing(_spacing) + {} + + void clear() + { + distance =3D 0; + spacing =3D 0; + } + double distance; double spacing; }; diff --git a/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.= cc b/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc index 16473f5..c1aa015 100644 --- a/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc +++ b/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc @@ -30,10 +30,12 @@ KisDynamicSensorSpeed::KisDynamicSensorSpeed() : KisDyn= amicSensor(SpeedId) } = qreal KisDynamicSensorSpeed::value(const KisPaintInformation& info) { - int dt =3D qMax(1, info.currentTime() - m_lastTime); // make sure dt >= 1 + int deltaTime =3D qMax(1, info.currentTime() - m_lastTime); // make su= re deltaTime > 1 m_lastTime =3D info.currentTime(); - double currentMove =3D info.movement().norm() / dt; - m_speed =3D qMin(1.0, (m_speed * 0.9 + currentMove * 0.1)); // average= it to get nicer result, at the price of being less mathematically correct,= but we quicly reach a situation where dt =3D 1 and currentMove =3D 1 + double currentMove =3D info.movement().norm() / deltaTime; + // Average it to get nicer result, at the price of being less mathemat= ically correct, + // but we quickly reach a situation where dt =3D 1 and currentMove =3D= 1 + m_speed =3D qMin(1.0, (m_speed * 0.9 + currentMove * 0.1)); return m_speed; } = diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.cc b/krita/plu= gins/tools/defaulttools/kis_tool_brush.cc index 536b81d..3b6491b 100644 --- a/krita/plugins/tools/defaulttools/kis_tool_brush.cc +++ b/krita/plugins/tools/defaulttools/kis_tool_brush.cc @@ -21,19 +21,22 @@ #include "kis_tool_brush.h" = #include +#include = #include = #include "kis_cursor.h" #include "kis_slider_spin_box.h" = - -#define MAXIMUM_SMOOTHNESS 1000 +#define MAXIMUM_SMOOTHNESS_QUALITY 100 // 0..100 +#define MAXIMUM_SMOOTHNESS_FACTOR 1000.0 // 0..1000.0 =3D=3D weight in gui #define MAXIMUM_MAGNETISM 1000 = = KisToolBrush::KisToolBrush(KoCanvasBase * canvas) - : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.pn= g", 5, 5), i18nc("(qtundo-format)", "Brush")) + : KisToolFreehand(canvas, + KisCursor::load("tool_freehand_cursor.png", 5, 5), + i18nc("(qtundo-format)", "Brush")) { setObjectName("tool_brush"); } @@ -42,9 +45,35 @@ KisToolBrush::~KisToolBrush() { } = -void KisToolBrush::slotSetSmoothness(int smoothness) +void KisToolBrush::slotSetSmoothingType(int index) { - m_smoothness =3D smoothness / (double)MAXIMUM_SMOOTHNESS; + switch (index) { + case 0: + m_smoothingOptions.smoothingType =3D KisSmoothingOptions::NO_SMOOT= HING; + m_sliderSmoothnessFactor->setEnabled(false); + m_sliderSmoothnessQuality->setEnabled(false); + break; + case 1: + m_smoothingOptions.smoothingType =3D KisSmoothingOptions::SIMPLE_S= MOOTHING; + m_sliderSmoothnessFactor->setEnabled(false); + m_sliderSmoothnessQuality->setEnabled(false); + break; + case 2: + default: + m_smoothingOptions.smoothingType =3D KisSmoothingOptions::WEIGHTED= _SMOOTHING; + m_sliderSmoothnessFactor->setEnabled(true); + m_sliderSmoothnessQuality->setEnabled(true); + } +} + +void KisToolBrush::slotSetSmoothnessQuality(int quality) +{ + m_smoothingOptions.smoothnessQuality =3D quality; +} + +void KisToolBrush::slotSetSmoothnessFactor(qreal factor) +{ + m_smoothingOptions.smoothnessFactor =3D factor; } = void KisToolBrush::slotSetMagnetism(int magnetism) @@ -54,23 +83,30 @@ void KisToolBrush::slotSetMagnetism(int magnetism) = QWidget * KisToolBrush::createOptionWidget() { - QWidget * optionWidget =3D KisToolFreehand::createOptionWidget(); optionWidget->setObjectName(toolId() + "option widget"); = - m_chkSmooth =3D new QCheckBox(i18nc("smooth out the curves while drawi= ng", "Smoothness:"), optionWidget); - m_chkSmooth->setObjectName("chkSmooth"); - m_chkSmooth->setChecked(m_smooth); - connect(m_chkSmooth, SIGNAL(toggled(bool)), this, SLOT(setSmooth(bool)= )); + // Line smoothing configuration + m_cmbSmoothingType =3D new QComboBox(optionWidget); + m_cmbSmoothingType->addItems(QStringList() << i18n("No Smoothing") << = i18n("Basic Smoothing") << i18n("Weighted Smoothing")); + m_cmbSmoothingType->setCurrentIndex(2); + connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SL= OT(slotSetSmoothingType(int))); + addOptionWidgetOption(m_cmbSmoothingType); + + m_sliderSmoothnessQuality =3D new KisSliderSpinBox(optionWidget); + m_sliderSmoothnessQuality->setRange(1, MAXIMUM_SMOOTHNESS_QUALITY); + m_sliderSmoothnessQuality->setEnabled(true); + connect(m_sliderSmoothnessQuality, SIGNAL(valueChanged(int)), SLOT(slo= tSetSmoothnessQuality(int))); + m_sliderSmoothnessQuality->setValue(m_smoothingOptions.smoothnessQuali= ty); + addOptionWidgetOption(m_sliderSmoothnessQuality, new QLabel(i18n("Qual= ity:"))); = - m_sliderSmoothness =3D new KisSliderSpinBox(optionWidget); - m_sliderSmoothness->setRange(0, MAXIMUM_SMOOTHNESS); - m_sliderSmoothness->setEnabled(true); - connect(m_chkSmooth, SIGNAL(toggled(bool)), m_sliderSmoothness, SLOT(s= etEnabled(bool))); - connect(m_sliderSmoothness, SIGNAL(valueChanged(int)), SLOT(slotSetSmo= othness(int))); - m_sliderSmoothness->setValue(m_smoothness * MAXIMUM_SMOOTHNESS); + m_sliderSmoothnessFactor =3D new KisDoubleSliderSpinBox(optionWidget); + m_sliderSmoothnessFactor->setRange(3.0, MAXIMUM_SMOOTHNESS_FACTOR, 1); + m_sliderSmoothnessFactor->setEnabled(true); + connect(m_sliderSmoothnessFactor, SIGNAL(valueChanged(qreal)), SLOT(sl= otSetSmoothnessFactor(qreal))); + m_sliderSmoothnessFactor->setValue(m_smoothingOptions.smoothnessFactor= ); = - addOptionWidgetOption(m_sliderSmoothness, m_chkSmooth); + addOptionWidgetOption(m_sliderSmoothnessFactor, new QLabel(i18n("Weigh= t:"))); = // Drawing assistant configuration m_chkAssistant =3D new QCheckBox(i18n("Assistant:"), optionWidget); @@ -78,7 +114,7 @@ QWidget * KisToolBrush::createOptionWidget() connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant= (bool))); m_sliderMagnetism =3D new KisSliderSpinBox(optionWidget); m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism")); - m_sliderMagnetism->setRange(0, MAXIMUM_SMOOTHNESS); + m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM); m_sliderMagnetism->setEnabled(false); connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT= (setEnabled(bool))); m_sliderMagnetism->setValue(m_magnetism * MAXIMUM_MAGNETISM); diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.h b/krita/plug= ins/tools/defaulttools/kis_tool_brush.h index f849233..dbf5c37 100644 --- a/krita/plugins/tools/defaulttools/kis_tool_brush.h +++ b/krita/plugins/tools/defaulttools/kis_tool_brush.h @@ -32,6 +32,7 @@ class QGridLayout; = class KoCanvasBase; class KisSliderSpinBox; +class KisDoubleSliderSpinBox; = class KisToolBrush : public KisToolFreehand { @@ -44,15 +45,19 @@ public: QWidget * createOptionWidget(); = private slots: - void slotSetSmoothness(int smoothness); + void slotSetSmoothnessQuality(int quality); + void slotSetSmoothnessFactor(qreal factor); void slotSetMagnetism(int magnetism); + void slotSetSmoothingType(int index); = private: QGridLayout *m_optionLayout; - QCheckBox *m_chkSmooth; + QComboBox *m_cmbSmoothingType; + QCheckBox *m_chkAssistant; KisSliderSpinBox *m_sliderMagnetism; - KisSliderSpinBox *m_sliderSmoothness; + KisDoubleSliderSpinBox *m_sliderSmoothnessFactor; + KisSliderSpinBox *m_sliderSmoothnessQuality; }; = = diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt index 2d8abc4..3eebb0a 100644 --- a/krita/ui/CMakeLists.txt +++ b/krita/ui/CMakeLists.txt @@ -147,6 +147,7 @@ set(kritaui_LIB_SRCS tool/kis_tool_polyline_base.cpp tool/kis_color_picker_utils.cpp tool/kis_resources_snapshot.cpp + tool/kis_smoothing_options.cpp tool/strokes/freehand_stroke.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp widgets/kis_channelflags_widget.cpp diff --git a/krita/image/kis_distance_information.h b/krita/ui/tool/kis_smo= othing_options.cpp similarity index 57% copy from krita/image/kis_distance_information.h copy to krita/ui/tool/kis_smoothing_options.cpp index c1ab409..14326c4 100644 --- a/krita/image/kis_distance_information.h +++ b/krita/ui/tool/kis_smoothing_options.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Cyrille Berger + * Copyright (c) 2012 Boudewijn Rempt * * 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 @@ -15,20 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-13= 01, USA. */ +#include "kis_smoothing_options.h" = -#ifndef _KIS_DISTANCE_INFORMATION_H_ -#define _KIS_DISTANCE_INFORMATION_H_ - -/** - * This function is used as return of paintLine to contains information th= at need - * to be passed for the next call. - */ -struct KisDistanceInformation { - KisDistanceInformation() : distance(0), spacing(0) {} - KisDistanceInformation(double _distance, double _spacing) : distance(_= distance), spacing(_spacing) {} - void clear() { distance =3D 0; spacing =3D 0;} - double distance; - double spacing; -}; - -#endif +KisSmoothingOptions::KisSmoothingOptions() + : smoothingType(WEIGHTED_SMOOTHING) + , smoothnessFactor(50.0) + , smoothnessQuality(20) +{ +} diff --git a/krita/image/kis_distance_information.h b/krita/ui/tool/kis_smo= othing_options.h similarity index 57% copy from krita/image/kis_distance_information.h copy to krita/ui/tool/kis_smoothing_options.h index c1ab409..b28144d 100644 --- a/krita/image/kis_distance_information.h +++ b/krita/ui/tool/kis_smoothing_options.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Cyrille Berger + * Copyright (c) 2012 Boudewijn Rempt * * 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 @@ -15,20 +15,26 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-13= 01, USA. */ +#ifndef KIS_SMOOTHING_OPTIONS_H +#define KIS_SMOOTHING_OPTIONS_H = -#ifndef _KIS_DISTANCE_INFORMATION_H_ -#define _KIS_DISTANCE_INFORMATION_H_ +#include + +struct KisSmoothingOptions +{ + + enum SmoothingType { + NO_SMOOTHING =3D 0, + SIMPLE_SMOOTHING, + WEIGHTED_SMOOTHING + }; + + KisSmoothingOptions(); + + SmoothingType smoothingType; + qreal smoothnessFactor; + int smoothnessQuality; = -/** - * This function is used as return of paintLine to contains information th= at need - * to be passed for the next call. - */ -struct KisDistanceInformation { - KisDistanceInformation() : distance(0), spacing(0) {} - KisDistanceInformation(double _distance, double _spacing) : distance(_= distance), spacing(_spacing) {} - void clear() { distance =3D 0; spacing =3D 0;} - double distance; - double spacing; }; = -#endif +#endif // KIS_SMOOTHING_OPTIONS_H diff --git a/krita/ui/tool/kis_tool_freehand.cc b/krita/ui/tool/kis_tool_fr= eehand.cc index 5640a4b..87595aa 100644 --- a/krita/ui/tool/kis_tool_freehand.cc +++ b/krita/ui/tool/kis_tool_freehand.cc @@ -71,9 +71,7 @@ KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, c= onst QCursor & cursor, { m_explicitShowOutline =3D false; = - m_smooth =3D true; m_assistant =3D false; - m_smoothness =3D 1.0; m_magnetism =3D 1.0; = setSupportOutline(true); @@ -160,7 +158,7 @@ void KisToolFreehand::initStroke(KoPointerEvent *event) { setCurrentNodeLocked(true); = - m_helper->setSmoothness(m_smooth, m_smoothness); + m_helper->setSmoothness(m_smoothingOptions); m_helper->initPaint(event, canvas()->resourceManager(), image(), image().data(), @@ -336,11 +334,6 @@ bool KisToolFreehand::wantsAutoScroll() const return false; } = -void KisToolFreehand::setSmooth(bool smooth) -{ - m_smooth =3D smooth; -} - void KisToolFreehand::setAssistant(bool assistant) { m_assistant =3D assistant; diff --git a/krita/ui/tool/kis_tool_freehand.h b/krita/ui/tool/kis_tool_fre= ehand.h index ee915d8..adc8445 100644 --- a/krita/ui/tool/kis_tool_freehand.h +++ b/krita/ui/tool/kis_tool_freehand.h @@ -25,6 +25,7 @@ #include "kis_resources_snapshot.h" #include "kis_paintop_settings.h" #include "kis_distance_information.h" +#include "kis_smoothing_options.h" = #include "krita_export.h" = @@ -83,7 +84,6 @@ protected: = protected slots: = - void setSmooth(bool smooth); void setAssistant(bool assistant); = private: @@ -114,8 +114,8 @@ private slots: void hideOutline(); = protected: - bool m_smooth; - double m_smoothness; + + KisSmoothingOptions m_smoothingOptions; bool m_assistant; double m_magnetism; = diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp b/krita/ui/tool/kis= _tool_freehand_helper.cpp index dde51ea..0d63072 100644 --- a/krita/ui/tool/kis_tool_freehand_helper.cpp +++ b/krita/ui/tool/kis_tool_freehand_helper.cpp @@ -30,7 +30,10 @@ #include "kis_recording_adapter.h" #include "kis_image.h" #include "kis_painter.h" +#include "kis_smoothing_options.h" = +#include +#include // for qIsNaN = struct KisToolFreehandHelper::Private { @@ -41,7 +44,6 @@ struct KisToolFreehandHelper::Private bool haveTangent; QPointF previousTangent; = - bool hasPaintAtLeastOnce; = QTime strokeTime; @@ -54,10 +56,12 @@ struct KisToolFreehandHelper::Private KisPaintInformation previousPaintInformation; KisPaintInformation olderPaintInformation; = - bool smooth; - qreal smoothness; + KisSmoothingOptions smoothingOptions; = QTimer airbrushingTimer; + + QList history; + QList velocityHistory; }; = = @@ -68,9 +72,6 @@ KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingI= nformationBuilder *info m_d->infoBuilder =3D infoBuilder; m_d->recordingAdapter =3D recordingAdapter; = - m_d->smooth =3D true; - m_d->smoothness =3D 1.0; - m_d->strokeTimeoutTimer.setSingleShot(true); connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke= ())); = @@ -82,10 +83,9 @@ KisToolFreehandHelper::~KisToolFreehandHelper() delete m_d; } = -void KisToolFreehandHelper::setSmoothness(bool smooth, qreal smoothness) +void KisToolFreehandHelper::setSmoothness(const KisSmoothingOptions &smoot= hingOptions) { - m_d->smooth =3D smooth; - m_d->smoothness =3D smoothness; + m_d->smoothingOptions =3D smoothingOptions; } = void KisToolFreehandHelper::initPaint(KoPointerEvent *event, @@ -99,7 +99,6 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *eve= nt, = m_d->strokesFacade =3D strokesFacade; = - m_d->haveTangent =3D false; m_d->previousTangent =3D QPointF(); = @@ -123,14 +122,18 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent = *event, } = KisStrokeStrategy *stroke =3D - new FreehandStrokeStrategy(indirectPainting, - m_d->resources, m_d->painterInfos, i18n= ("Freehand Stroke")); + new FreehandStrokeStrategy(indirectPainting, + m_d->resources, m_d->painterInfos, = i18n("Freehand Stroke")); = m_d->strokeId =3D m_d->strokesFacade->startStroke(stroke); = m_d->previousPaintInformation =3D - m_d->infoBuilder->startStroke(event, m_d->strokeTime.elapsed()); + m_d->infoBuilder->startStroke(event, m_d->strokeTime.elapsed()= ); = + m_d->history.clear(); + m_d->history.append(m_d->previousPaintInformation); + m_d->velocityHistory.clear(); + m_d->velocityHistory.append(std::numeric_limits::signaling_NaN(= )); if(m_d->resources->needsAirbrushing()) { m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate(= )); m_d->airbrushingTimer.start(); @@ -140,19 +143,93 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent = *event, void KisToolFreehandHelper::paint(KoPointerEvent *event) { KisPaintInformation info =3D - m_d->infoBuilder->continueStroke(event, - m_d->previousPaintInformation.pos= (), - m_d->strokeTime.elapsed()); + m_d->infoBuilder->continueStroke(event, + m_d->previousPaintInformation= .pos(), + m_d->strokeTime.elapsed()); + + // Smooth the coordinates out using the history and the velocity. See + // https://bugs.kde.org/show_bug.cgi?id=3D281267 and http://www24.atwi= ki.jp/sigetch_2007/pages/17.html. + // This is also implemented in gimp, which is where I cribbed the code= from. + if (m_d->smoothingOptions.smoothingType =3D=3D KisSmoothingOptions::WE= IGHTED_SMOOTHING + && m_d->smoothingOptions.smoothnessQuality > 1 + && m_d->smoothingOptions.smoothnessFactor > 3.0) { + + m_d->history.append(info); + m_d->velocityHistory.append(std::numeric_limits::signaling_= NaN()); // Fake velocity! + + qreal x =3D 0.0; + qreal y =3D 0.0; + + if (m_d->history.size() > 3) { + + int length =3D qMin(m_d->smoothingOptions.smoothnessQuality, m= _d->history.size()); + int minIndex =3D m_d->history.size() - length; + + qreal gaussianWeight =3D 0.0; + qreal gaussianWeight2 =3D m_d->smoothingOptions.smoothnessFact= or * m_d->smoothingOptions.smoothnessFactor; + qreal velocitySum =3D 0.0; + qreal scaleSum =3D 0.0; + + if (gaussianWeight2 !=3D 0.0) { + gaussianWeight =3D 1 / (sqrt(2 * M_PI) * m_d->smoothingOpt= ions.smoothnessFactor); + } + + Q_ASSERT(m_d->history.size() =3D=3D m_d->velocityHistory.size(= )); + + for (int i =3D m_d->history.size() - 1; i >=3D minIndex; i--) { + qreal rate =3D 0.0; + + const KisPaintInformation nextInfo =3D m_d->history.at(i); + double velocity =3D m_d->velocityHistory.at(i); + + if (qIsNaN(velocity)) { + + int previousTime =3D nextInfo.currentTime(); + if (i > 0) { + previousTime =3D m_d->history.at(i - 1).currentTim= e(); + } + + int deltaTime =3D qMax(1, nextInfo.currentTime() - pre= viousTime); // make sure deltaTime > 1 + velocity =3D info.movement().norm() / deltaTime; + m_d->velocityHistory[i] =3D velocity; + } + + if (gaussianWeight2 !=3D 0.0) { + velocitySum +=3D velocity * 100; + rate =3D gaussianWeight * exp(-velocitySum * velocityS= um / (2 * gaussianWeight2)); + } + scaleSum +=3D rate; + x +=3D rate * nextInfo.pos().x(); + y +=3D rate * nextInfo.pos().y(); + } + + if (scaleSum !=3D 0.0) { + x /=3D scaleSum; + y /=3D scaleSum; + } + if ((x !=3D 0.0 && y !=3D 0.0) || (x =3D=3D info.pos().x() && = y =3D=3D info.pos().y())) { + m_d->history.last().setPos(QPointF(x, y)); + info.setPos(QPointF(x, y)); + } + } + } = - if (m_d->smooth) { + if (m_d->smoothingOptions.smoothingType =3D=3D KisSmoothingOptions::SI= MPLE_SMOOTHING + || m_d->smoothingOptions.smoothingType =3D=3D KisSmoothingOpti= ons::WEIGHTED_SMOOTHING) + { + // Now paint between the coordinates, using the bezier curve inter= polation if (!m_d->haveTangent) { m_d->haveTangent =3D true; + + // XXX: 3.0 is a magic number I don't know anything about + // 1.0 was the old default value for smoothness, and anyt= hing lower than that + // gave horrible results, so remove that setting. m_d->previousTangent =3D - (info.pos() - m_d->previousPaintInformation.pos()) * m_d->= smoothness / - (3.0 * (info.currentTime() - m_d->previousPaintInformation= .currentTime())); + (info.pos() - m_d->previousPaintInformation.pos()) / + (3.0 * (info.currentTime() - m_d->previousPaintInforma= tion.currentTime())); } else { - QPointF newTangent =3D (info.pos() - m_d->olderPaintInformatio= n.pos()) * m_d->smoothness / - (3.0 * (info.currentTime() - m_d->olderP= aintInformation.currentTime())); + QPointF newTangent =3D (info.pos() - m_d->olderPaintInformatio= n.pos()) / + (3.0 * (info.currentTime() - m_d->olderPaintInformatio= n.currentTime())); qreal scaleFactor =3D (m_d->previousPaintInformation.currentTi= me() - m_d->olderPaintInformation.currentTime()); QPointF control1 =3D m_d->olderPaintInformation.pos() + m_d->p= reviousTangent * scaleFactor; QPointF control2 =3D m_d->previousPaintInformation.pos() - new= Tangent * scaleFactor; @@ -165,10 +242,13 @@ void KisToolFreehandHelper::paint(KoPointerEvent *eve= nt) } m_d->olderPaintInformation =3D m_d->previousPaintInformation; m_d->strokeTimeoutTimer.start(100); - } else { + } + else { paintLine(m_d->painterInfos, m_d->previousPaintInformation, info); } = + + m_d->previousPaintInformation =3D info; = if(m_d->airbrushingTimer.isActive()) { @@ -180,7 +260,7 @@ void KisToolFreehandHelper::endPaint() { if (!m_d->hasPaintAtLeastOnce) { paintAt(m_d->painterInfos, m_d->previousPaintInformation); - } else if (m_d->smooth) { + } else if (m_d->smoothingOptions.smoothingType !=3D KisSmoothingOption= s::NO_SMOOTHING) { finishStroke(); } m_d->strokeTimeoutTimer.stop(); @@ -215,7 +295,7 @@ void KisToolFreehandHelper::finishStroke() if(m_d->haveTangent) { m_d->haveTangent =3D false; = - QPointF newTangent =3D (m_d->previousPaintInformation.pos() - m_d-= >olderPaintInformation.pos()) * m_d->smoothness / 3.0; + QPointF newTangent =3D (m_d->previousPaintInformation.pos() - m_d-= >olderPaintInformation.pos()) / 3.0; qreal scaleFactor =3D (m_d->previousPaintInformation.currentTime()= - m_d->olderPaintInformation.currentTime()); QPointF control1 =3D m_d->olderPaintInformation.pos() + m_d->previ= ousTangent * scaleFactor; QPointF control2 =3D m_d->previousPaintInformation.pos() - newTang= ent; @@ -239,8 +319,8 @@ void KisToolFreehandHelper::paintAt(PainterInfo *painte= rInfo, { m_d->hasPaintAtLeastOnce =3D true; m_d->strokesFacade->addJob(m_d->strokeId, - new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), - painterInfo, pi)); + new FreehandStrokeStrategy::Data(m_d->resou= rces->currentNode(), + painterInf= o, pi)); = if(m_d->recordingAdapter) { m_d->recordingAdapter->addPoint(pi); @@ -253,8 +333,8 @@ void KisToolFreehandHelper::paintLine(PainterInfo *pain= terInfo, { m_d->hasPaintAtLeastOnce =3D true; m_d->strokesFacade->addJob(m_d->strokeId, - new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), - painterInfo, pi1, pi2)); + new FreehandStrokeStrategy::Data(m_d->resou= rces->currentNode(), + painterInf= o, pi1, pi2)); = if(m_d->recordingAdapter) { m_d->recordingAdapter->addLine(pi1, pi2); @@ -269,9 +349,9 @@ void KisToolFreehandHelper::paintBezierCurve(PainterInf= o *painterInfo, { m_d->hasPaintAtLeastOnce =3D true; m_d->strokesFacade->addJob(m_d->strokeId, - new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), - painterInfo, - pi1, control1, control2, pi2)); + new FreehandStrokeStrategy::Data(m_d->resou= rces->currentNode(), + painterInf= o, + pi1, contr= ol1, control2, pi2)); = if(m_d->recordingAdapter) { m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2); diff --git a/krita/ui/tool/kis_tool_freehand_helper.h b/krita/ui/tool/kis_t= ool_freehand_helper.h index 4978c85..ebfd526 100644 --- a/krita/ui/tool/kis_tool_freehand_helper.h +++ b/krita/ui/tool/kis_tool_freehand_helper.h @@ -35,21 +35,23 @@ class KisStrokesFacade; class KisPostExecutionUndoAdapter; class KisPaintOp; class KisPainter; - +class KisSmoothingOptions; = class KRITAUI_EXPORT KisToolFreehandHelper : public QObject { Q_OBJECT = protected: + typedef FreehandStrokeStrategy::PainterInfo PainterInfo; = public: + KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, KisRecordingAdapter *recordingAdapter =3D 0); ~KisToolFreehandHelper(); = - void setSmoothness(bool smooth, qreal smoothness); + void setSmoothness(const KisSmoothingOptions &smoothingOptions); = void initPaint(KoPointerEvent *event, KoCanvasResourceManager *resourceManager, @@ -63,6 +65,7 @@ public: const KisPaintOp* currentPaintOp() const; = protected: + virtual void createPainters(QVector &painterInfos); = virtual void paintAt(const QVector &painterInfos, @@ -78,7 +81,7 @@ protected: const QPointF &control2, const KisPaintInformation &pi2); = -protected: + void paintAt(PainterInfo *painterInfo, const KisPaintInformation &pi); = void paintLine(PainterInfo *painterInfo, @@ -92,6 +95,7 @@ protected: const KisPaintInformation &pi2); = private slots: + void finishStroke(); void doAirbrushing(); =20