[krita/krita/3.0] libs/ui: Fix "bended lines" stabilizer problem on Windows
Boudewijn Rempt
boud at valdyas.org
Tue May 31 07:35:52 UTC 2016
Git commit da5496d843237daefd214a7849d2cad0f818b43b by Boudewijn Rempt, on behalf of Dmitry Kazakov.
Committed on 31/05/2016 at 07:35.
Pushed by rempt into branch 'krita/3.0'.
Fix "bended lines" stabilizer problem on Windows
The problem is that on Windows the tablet events are coming in bunches,
not uniformly. Therefore any timing-based smoothing system will not work
out of box.
This patch adds a special class KisStabilizedEventsSampler, that makes
the events uniform. It collects a set of events on a 50ms timeframe and
then distributes it uniformly.
The timeframe size should correlate with the maximum size of the delays
created by the events system. On Windows it is 50ms, on Linux 15-20ms.
The timeframe can be configured with "stabilizerSampleSize" config option.
BUG:362445
Ref T2414
CC:kimageshop at kde.org
M +1 -0 libs/ui/CMakeLists.txt
M +18 -1 libs/ui/kis_config.cc
M +4 -0 libs/ui/kis_config.h
M +6 -0 libs/ui/tests/CMakeLists.txt
A +64 -0 libs/ui/tests/kis_stabilized_events_sampler_test.cpp [License: GPL (v2+)]
A +31 -0 libs/ui/tests/kis_stabilized_events_sampler_test.h [License: GPL (v2+)]
A +103 -0 libs/ui/tool/kis_stabilized_events_sampler.cpp [License: GPL (v2+)]
A +90 -0 libs/ui/tool/kis_stabilized_events_sampler.h [License: GPL (v2+)]
M +42 -28 libs/ui/tool/kis_tool_freehand_helper.cpp
http://commits.kde.org/krita/da5496d843237daefd214a7849d2cad0f818b43b
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index a88c7b7..b91d0c7 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -182,6 +182,7 @@ set(kritaui_LIB_SRCS
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
+ tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc
index 119f6fa..064e25e 100644
--- a/libs/ui/kis_config.cc
+++ b/libs/ui/kis_config.cc
@@ -1104,7 +1104,7 @@ void KisConfig::setHideStatusbarFullscreen(const bool value) const
bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const
{
-#ifdef Q_OS_WINDOWS
+#ifdef Q_OS_WIN
return false;
#else
return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true));
@@ -1655,3 +1655,20 @@ void KisConfig::setConvertToImageColorspaceOnImport(bool value)
{
m_cfg.writeEntry("ConvertToImageColorSpaceOnImport", value);
}
+
+int KisConfig::stabilizerSampleSize(bool defaultValue) const
+{
+#ifdef Q_OS_WIN
+ const int defaultSampleSize = 50;
+#else
+ const int defaultSampleSize = 15;
+#endif
+
+ return defaultValue ?
+ defaultSampleSize : m_cfg.readEntry("stabilizerSampleSize", defaultSampleSize);
+}
+
+void KisConfig::setStabilizerSampleSize(int value)
+{
+ m_cfg.writeEntry("stabilizerSampleSize", value);
+}
diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h
index afb513f..3776859 100644
--- a/libs/ui/kis_config.h
+++ b/libs/ui/kis_config.h
@@ -470,6 +470,10 @@ public:
bool convertToImageColorspaceOnImport(bool defaultValue = false) const;
void setConvertToImageColorspaceOnImport(bool value);
+ int stabilizerSampleSize(bool defaultValue = false) const;
+ void setStabilizerSampleSize(int value);
+
+
template<class T>
void writeEntry(const QString& name, const T& value) {
m_cfg.writeEntry(name, value);
diff --git a/libs/ui/tests/CMakeLists.txt b/libs/ui/tests/CMakeLists.txt
index 67951c1..7219220 100644
--- a/libs/ui/tests/CMakeLists.txt
+++ b/libs/ui/tests/CMakeLists.txt
@@ -253,3 +253,9 @@ target_link_libraries(KisAnimationFrameCacheTest kritaui kritaimage ${QT_QTTEST_
set(ResourceBundleTest_SRCS ResourceBundleTest.cpp)
kde4_add_broken_unit_test(ResourceBundleTest TESTNAME krita-resourcemanager-ResourceBundleTest ${ResourceBundleTest_SRCS})
target_link_libraries(ResourceBundleTest kritaui kritalibbrush kritalibpaintop Qt5::Test )
+
+########### next target ###############
+
+set(kis_stabilized_events_sampler_test_SRCS kis_stabilized_events_sampler_test.cpp)
+kde4_add_unit_test(KisStabilizedEventsSamplerTest TESTNAME krita-ui-StabilizedEventsSamplerTest ${kis_stabilized_events_sampler_test_SRCS})
+target_link_libraries(KisStabilizedEventsSamplerTest kritaui Qt5::Test)
diff --git a/libs/ui/tests/kis_stabilized_events_sampler_test.cpp b/libs/ui/tests/kis_stabilized_events_sampler_test.cpp
new file mode 100644
index 0000000..cbd3deb
--- /dev/null
+++ b/libs/ui/tests/kis_stabilized_events_sampler_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 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_stabilized_events_sampler_test.h"
+
+#include "kis_stabilized_events_sampler.h"
+#include "kis_paint_information.h"
+
+void KisStabilizedEventsSamplerTest::test()
+{
+ KisStabilizedEventsSampler sampler(20);
+
+ KisPaintInformation pi1(QPoint(10,10));
+ KisPaintInformation pi2(QPoint(20,20));
+
+ sampler.addEvent(pi1);
+
+ QTest::qSleep(50);
+
+ sampler.addEvent(pi2);
+
+ QTest::qSleep(70);
+
+ KisStabilizedEventsSampler::iterator it;
+ KisStabilizedEventsSampler::iterator end;
+ std::tie(it, end) = sampler.range();
+
+
+ int numTotal = 0;
+ int num1 = 0;
+ int num2 = 0;
+
+ for (; it != end; ++it) {
+ numTotal++;
+ if (it->pos().x() == 10) {
+ num1++;
+ } else if (it->pos().x() == 20) {
+ num2++;
+ }
+
+ qDebug() << ppVar(it->pos());
+ }
+
+ QVERIFY(numTotal >= 6);
+ QVERIFY(num1 >= 3);
+ QVERIFY(num2 >= 3);
+}
+
+QTEST_MAIN(KisStabilizedEventsSamplerTest)
diff --git a/libs/ui/tests/kis_stabilized_events_sampler_test.h b/libs/ui/tests/kis_stabilized_events_sampler_test.h
new file mode 100644
index 0000000..885ea2b
--- /dev/null
+++ b/libs/ui/tests/kis_stabilized_events_sampler_test.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 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_STABILIZED_EVENTS_SAMPLER_TEST_H
+#define __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H
+
+#include <QtTest/QtTest>
+
+class KisStabilizedEventsSamplerTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void test();
+};
+
+#endif /* __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H */
diff --git a/libs/ui/tool/kis_stabilized_events_sampler.cpp b/libs/ui/tool/kis_stabilized_events_sampler.cpp
new file mode 100644
index 0000000..2d47f4b
--- /dev/null
+++ b/libs/ui/tool/kis_stabilized_events_sampler.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 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_stabilized_events_sampler.h"
+
+#include <QList>
+#include <QElapsedTimer>
+#include <QtMath>
+
+#include "kis_paint_information.h"
+
+
+struct KisStabilizedEventsSampler::Private
+{
+ Private(int _sampleTime) : sampleTime(_sampleTime) {}
+
+ std::function<void(const KisPaintInformation &)> paintLine;
+ QElapsedTimer lastPaintTime;
+ QList<KisPaintInformation> realEvents;
+ int sampleTime;
+
+ KisPaintInformation lastPaintInformation;
+};
+
+KisStabilizedEventsSampler::KisStabilizedEventsSampler(int sampleTime)
+ : m_d(new Private(sampleTime))
+{
+}
+
+KisStabilizedEventsSampler::~KisStabilizedEventsSampler()
+{
+}
+
+void KisStabilizedEventsSampler::setLineFunction(std::function<void(const KisPaintInformation &)> func)
+{
+ m_d->paintLine = func;
+}
+
+void KisStabilizedEventsSampler::clear()
+{
+ if (!m_d->realEvents.isEmpty()) {
+ m_d->lastPaintInformation = m_d->realEvents.last();
+ }
+
+ m_d->realEvents.clear();
+ m_d->lastPaintTime.start();
+}
+
+void KisStabilizedEventsSampler::addEvent(const KisPaintInformation &pi)
+{
+ if (!m_d->lastPaintTime.isValid()) {
+ m_d->lastPaintTime.start();
+ }
+
+ m_d->realEvents.append(pi);
+}
+
+void KisStabilizedEventsSampler::processAllEvents()
+{
+ const int elapsed = m_d->lastPaintTime.restart();
+
+ const qreal alpha = qreal(m_d->realEvents.size()) / elapsed;
+
+ for (int i = 0; i < elapsed; i += m_d->sampleTime) {
+ const int k = qFloor(alpha * i);
+
+ m_d->paintLine(m_d->realEvents[k]);
+ }
+}
+
+const KisPaintInformation& KisStabilizedEventsSampler::iterator::dereference() const
+{
+ const int k = qFloor(m_alpha * m_index);
+ return k < m_sampler->m_d->realEvents.size() ?
+ m_sampler->m_d->realEvents[k] : m_sampler->m_d->lastPaintInformation;
+}
+
+std::pair<KisStabilizedEventsSampler::iterator, KisStabilizedEventsSampler::iterator>
+KisStabilizedEventsSampler::range() const
+{
+ const int elapsed = m_d->lastPaintTime.restart() / m_d->sampleTime;
+ const qreal alpha = qreal(m_d->realEvents.size()) / elapsed;
+
+ return std::make_pair(iterator(this, 0, alpha),
+ iterator(this, elapsed, alpha));
+}
+
+
diff --git a/libs/ui/tool/kis_stabilized_events_sampler.h b/libs/ui/tool/kis_stabilized_events_sampler.h
new file mode 100644
index 0000000..feb6fc6
--- /dev/null
+++ b/libs/ui/tool/kis_stabilized_events_sampler.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 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_STABILIZED_EVENTS_SAMPLER_H
+#define __KIS_STABILIZED_EVENTS_SAMPLER_H
+
+#include <QScopedPointer>
+
+#include <functional>
+#include <boost/iterator/iterator_facade.hpp>
+
+#include "kritaui_export.h"
+
+class KisPaintInformation;
+#include <kis_paint_information.h>
+
+
+class KRITAUI_EXPORT KisStabilizedEventsSampler
+{
+public:
+ KisStabilizedEventsSampler(int sampleTime = 1);
+ ~KisStabilizedEventsSampler();
+
+ void setLineFunction(std::function<void(const KisPaintInformation &)> func);
+
+ void clear();
+ void addEvent(const KisPaintInformation &pi);
+ void processAllEvents();
+
+public:
+ class iterator :
+ public boost::iterator_facade <iterator,
+ KisPaintInformation const,
+ boost::forward_traversal_tag >
+ {
+ public:
+ iterator()
+ : m_sampler(0),
+ m_index(0),
+ m_alpha(0) {}
+
+ iterator(const KisStabilizedEventsSampler* sampler, int index, qreal alpha)
+ : m_sampler(sampler),
+ m_index(index),
+ m_alpha(alpha) {}
+
+ private:
+ friend class boost::iterator_core_access;
+
+ void increment() {
+ m_index++;
+ }
+
+ bool equal(iterator const& other) const {
+ return m_index == other.m_index &&
+ m_sampler == other.m_sampler;
+ }
+
+ const KisPaintInformation& dereference() const;
+
+ private:
+ const KisStabilizedEventsSampler* m_sampler;
+ int m_index;
+ qreal m_alpha;
+ };
+
+ std::pair<iterator, iterator> range() const;
+
+private:
+ struct Private;
+ const QScopedPointer<Private> m_d;
+};
+
+
+#endif /* __KIS_STABILIZED_EVENTS_SAMPLER_H */
diff --git a/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp
index 20d30a7..dde3424 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.cpp
+++ b/libs/ui/tool/kis_tool_freehand_helper.cpp
@@ -35,6 +35,8 @@
#include <brushengine/kis_paintop_utils.h>
#include "kis_update_time_monitor.h"
+#include "kis_stabilized_events_sampler.h"
+#include "kis_config.h"
#include <math.h>
@@ -75,8 +77,8 @@ struct KisToolFreehandHelper::Private
// Stabilizer data
QQueue<KisPaintInformation> stabilizerDeque;
- KisPaintInformation stabilizerLastPaintInfo;
QTimer stabilizerPollTimer;
+ KisStabilizedEventsSampler stabilizedSampler;
int canvasRotation;
bool canvasMirroredH;
@@ -516,7 +518,7 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
- m_d->stabilizerLastPaintInfo = info;
+ m_d->stabilizedSampler.addEvent(info);
} else {
m_d->previousPaintInformation = info;
}
@@ -601,11 +603,13 @@ void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
for (int i = sampleSize; i > 0; i--) {
m_d->stabilizerDeque.enqueue(firstPaintInfo);
}
- m_d->stabilizerLastPaintInfo = firstPaintInfo;
- // Poll and draw each millisecond
- m_d->stabilizerPollTimer.setInterval(1);
+ // Poll and draw regularly
+ KisConfig cfg;
+ m_d->stabilizerPollTimer.setInterval(cfg.stabilizerSampleSize());
m_d->stabilizerPollTimer.start();
+
+ m_d->stabilizedSampler.clear();
}
KisPaintInformation
@@ -646,41 +650,51 @@ KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue<KisPaintInfo
void KisToolFreehandHelper::stabilizerPollAndPaint()
{
- KisPaintInformation newInfo =
- m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, m_d->stabilizerLastPaintInfo);
+ KisStabilizedEventsSampler::iterator it;
+ KisStabilizedEventsSampler::iterator end;
+ std::tie(it, end) = m_d->stabilizedSampler.range();
- bool canPaint = true;
+ for (; it != end; ++it) {
+ KisPaintInformation sampledInfo = *it;
- if (m_d->smoothingOptions->useDelayDistance()) {
- const qreal R = m_d->smoothingOptions->delayDistance() /
- m_d->resources->effectiveZoom();
+ bool canPaint = true;
- QPointF diff = m_d->stabilizerLastPaintInfo.pos() - m_d->previousPaintInformation.pos();
- qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
+ if (m_d->smoothingOptions->useDelayDistance()) {
+ const qreal R = m_d->smoothingOptions->delayDistance() /
+ m_d->resources->effectiveZoom();
- canPaint = dx > R;
- }
+ QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
+ qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
+
+ canPaint = dx > R;
+ }
+
+ if (canPaint) {
+ KisPaintInformation newInfo =
+ m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo);
- if (canPaint) {
- paintLine(m_d->previousPaintInformation, newInfo);
- m_d->previousPaintInformation = newInfo;
+ paintLine(m_d->previousPaintInformation, newInfo);
+ m_d->previousPaintInformation = newInfo;
- // Push the new entry through the queue
- m_d->stabilizerDeque.dequeue();
- m_d->stabilizerDeque.enqueue(m_d->stabilizerLastPaintInfo);
+ // Push the new entry through the queue
+ m_d->stabilizerDeque.dequeue();
+ m_d->stabilizerDeque.enqueue(sampledInfo);
- emit requestExplicitUpdateOutline();
- } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
+ emit requestExplicitUpdateOutline();
+ } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
- QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
- QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();
+ QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
+ QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();
- while (it != end) {
- *it = m_d->previousPaintInformation;
- ++it;
+ while (it != end) {
+ *it = m_d->previousPaintInformation;
+ ++it;
+ }
}
}
+
+ m_d->stabilizedSampler.clear();
}
void KisToolFreehandHelper::stabilizerEnd()
More information about the kimageshop
mailing list