Krita Source Code Documentation
Loading...
Searching...
No Matches
KisToolFreehandHelper Class Reference

#include <kis_tool_freehand_helper.h>

+ Inheritance diagram for KisToolFreehandHelper:

Classes

struct  Private
 

Signals

void requestExplicitUpdateOutline ()
 

Public Member Functions

void cursorMoved (const QPointF &cursorPos)
 
void endPaint ()
 
void initPaint (KoPointerEvent *event, const QPointF &pixelCoords, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
 
bool isRunning () const
 
 KisToolFreehandHelper (KisPaintingInformationBuilder *infoBuilder, KoCanvasResourceProvider *resourceManager, const KUndo2MagicString &transactionText=KUndo2MagicString(), KisSmoothingOptions *smoothingOptions=0)
 
void paintEvent (KoPointerEvent *event)
 
KisOptimizedBrushOutline paintOpOutline (const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettingsSP globalSettings, KisPaintOpSettings::OutlineMode mode) const
 
void setSmoothness (KisSmoothingOptionsSP smoothingOptions)
 
KisSmoothingOptionsSP smoothingOptions () const
 
 ~KisToolFreehandHelper () override
 

Protected Member Functions

void cancelPaint ()
 
virtual void createPainters (QVector< KisFreehandStrokeInfo * > &strokeInfos, const KisDistanceInformation &startDist)
 
int elapsedStrokeTime () const
 
void initPaintImpl (qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
 
virtual void paintAt (const KisPaintInformation &pi)
 
void paintAt (int strokeInfoId, const KisPaintInformation &pi)
 
virtual void paintBezierCurve (const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)
 
void paintBezierCurve (int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)
 
virtual void paintLine (const KisPaintInformation &pi1, const KisPaintInformation &pi2)
 
void paintLine (int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2)
 
KoCanvasResourceProviderresourceManager () const
 

Private Slots

void doAirbrushing ()
 
void finishStroke ()
 
void slotSmoothingTypeChanged ()
 
void stabilizerPollAndPaint ()
 

Private Member Functions

int computeAirbrushTimerInterval () const
 
qreal currentPhysicalZoom () const
 
qreal currentZoom () const
 
KisPaintInformation getStabilizedPaintInfo (const QQueue< KisPaintInformation > &queue, const KisPaintInformation &lastPaintInfo)
 
void paint (KisPaintInformation &info)
 
void paintBezierSegment (KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2)
 
void stabilizerEnd ()
 
void stabilizerStart (KisPaintInformation firstPaintInfo)
 

Private Attributes

Private *const m_d
 

Detailed Description

Definition at line 31 of file kis_tool_freehand_helper.h.

Constructor & Destructor Documentation

◆ KisToolFreehandHelper()

KisToolFreehandHelper::KisToolFreehandHelper ( KisPaintingInformationBuilder * infoBuilder,
KoCanvasResourceProvider * resourceManager,
const KUndo2MagicString & transactionText = KUndo2MagicString(),
KisSmoothingOptions * smoothingOptions = 0 )

Definition at line 115 of file kis_tool_freehand_helper.cpp.

119 : m_d(new Private())
120{
122 m_d->infoBuilder = infoBuilder;
123 m_d->transactionText = transactionText;
126
129
130 m_d->strokeTimeoutTimer.setSingleShot(true);
131 connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
132 connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
133 connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
134 connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged()));
135
137 [this](const KisPaintInformation &pi1, const KisPaintInformation &pi2) {
138 paintLine(pi1, pi2);
139 });
141 [this]() {
143 });
144}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void setUpdateOutlineCallback(std::function< void()> requestUpdateOutline)
void setPaintLineCallback(std::function< void(const KisPaintInformation &, const KisPaintInformation &)> paintLine)
KisSmoothingOptionsSP smoothingOptions() const
KoCanvasResourceProvider * resourceManager() const
void paintLine(int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2)
void requestExplicitUpdateOutline()
QSharedPointer< KisSmoothingOptions > KisSmoothingOptionsSP
KisPerStrokeRandomSourceSP fakeStrokeRandomSource
KoCanvasResourceProvider * resourceManager
KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper
KisPaintingInformationBuilder * infoBuilder

◆ ~KisToolFreehandHelper()

KisToolFreehandHelper::~KisToolFreehandHelper ( )
override

Definition at line 146 of file kis_tool_freehand_helper.cpp.

147{
148 delete m_d;
149}

References m_d.

Member Function Documentation

◆ cancelPaint()

void KisToolFreehandHelper::cancelPaint ( )
protected

Definition at line 719 of file kis_tool_freehand_helper.cpp.

720{
721 if (!m_d->strokeId) return;
722
723 m_d->strokeTimeoutTimer.stop();
724
725 if (m_d->airbrushingTimer.isActive()) {
726 m_d->airbrushingTimer.stop();
727 }
728
731 }
732
733 if (m_d->stabilizerPollTimer.isActive()) {
734 m_d->stabilizerPollTimer.stop();
735 }
736
739 }
740
741 // see a comment in endPaint()
742 m_d->strokeInfos.clear();
743
745 m_d->strokeId.clear();
746
747}
virtual bool cancelStroke(KisStrokeId id)=0
QVector< KisFreehandStrokeInfo * > strokeInfos
KisAsynchronousStrokeUpdateHelper asyncUpdateHelper

References KisToolFreehandHelper::Private::airbrushingTimer, KisToolFreehandHelper::Private::asyncUpdateHelper, KisStabilizerDelayedPaintHelper::cancel(), KisStrokesFacade::cancelStroke(), KisAsynchronousStrokeUpdateHelper::cancelUpdateStream(), KisAsynchronousStrokeUpdateHelper::isActive(), m_d, KisStabilizerDelayedPaintHelper::running(), KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::stabilizerPollTimer, KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokeInfos, KisToolFreehandHelper::Private::strokesFacade, and KisToolFreehandHelper::Private::strokeTimeoutTimer.

◆ computeAirbrushTimerInterval()

int KisToolFreehandHelper::computeAirbrushTimerInterval ( ) const
private

Definition at line 969 of file kis_tool_freehand_helper.cpp.

970{
972 return qMax(1, qFloor(realInterval));
973}
const qreal AIRBRUSH_INTERVAL_FACTOR

References AIRBRUSH_INTERVAL_FACTOR, KisResourcesSnapshot::airbrushingInterval(), m_d, and KisToolFreehandHelper::Private::resources.

◆ createPainters()

void KisToolFreehandHelper::createPainters ( QVector< KisFreehandStrokeInfo * > & strokeInfos,
const KisDistanceInformation & startDist )
protectedvirtual

Reimplemented in KisToolMultihandHelper.

Definition at line 1042 of file kis_tool_freehand_helper.cpp.

1044{
1045 strokeInfos << new KisFreehandStrokeInfo(startDist);
1046}

◆ currentPhysicalZoom()

qreal KisToolFreehandHelper::currentPhysicalZoom ( ) const
private

◆ currentZoom()

qreal KisToolFreehandHelper::currentZoom ( ) const
private

Definition at line 975 of file kis_tool_freehand_helper.cpp.

976{
978}
@ EffectiveZoom
-Used only by painting tools for non-displaying purposes

References KoCanvasResource::EffectiveZoom, m_d, KoCanvasResourceProvider::resource(), and KisToolFreehandHelper::Private::resourceManager.

◆ cursorMoved()

void KisToolFreehandHelper::cursorMoved ( const QPointF & cursorPos)

◆ doAirbrushing

void KisToolFreehandHelper::doAirbrushing ( )
privateslot

Definition at line 945 of file kis_tool_freehand_helper.cpp.

946{
947 // Check that the stroke hasn't ended.
948 if (!m_d->strokeInfos.isEmpty()) {
949
950 // Add a new painting update at a point identical to the previous one, except for the time
951 // and speed information.
953 KisPaintInformation nextPaint(prevPaint.pos(),
954 prevPaint.pressure(),
955 prevPaint.xTilt(),
956 prevPaint.yTilt(),
957 prevPaint.rotation(),
958 prevPaint.tangentialPressure(),
959 prevPaint.perspective(),
961 0.0);
962 nextPaint.setCanvasRotation(prevPaint.canvasRotation());
963 nextPaint.setCanvasMirroredH(prevPaint.canvasMirroredH());
964 nextPaint.setCanvasMirroredV(prevPaint.canvasMirroredV());
965 paint(nextPaint);
966 }
967}
qreal perspective() const
reciprocal of distance on the perspective grid
const QPointF & pos() const
qreal xTilt() const
The tilt of the pen on the horizontal axis (from 0.0 to 1.0)
qreal tangentialPressure() const
tangential pressure (i.e., rate for an airbrush device)
qreal yTilt() const
The tilt of the pen on the vertical axis (from 0.0 to 1.0)
qreal rotation() const
rotation as given by the tablet event
qreal pressure() const
The pressure of the value (from 0.0 to 1.0)
void paint(KisPaintInformation &info)

References KisPaintInformation::canvasMirroredH(), KisPaintInformation::canvasMirroredV(), KisPaintInformation::canvasRotation(), elapsedStrokeTime(), m_d, paint(), KisPaintInformation::perspective(), KisPaintInformation::pos(), KisPaintInformation::pressure(), KisToolFreehandHelper::Private::previousPaintInformation, KisPaintInformation::rotation(), KisPaintInformation::setCanvasMirroredH(), KisPaintInformation::setCanvasMirroredV(), KisPaintInformation::setCanvasRotation(), KisToolFreehandHelper::Private::strokeInfos, KisPaintInformation::tangentialPressure(), KisPaintInformation::xTilt(), and KisPaintInformation::yTilt().

◆ elapsedStrokeTime()

int KisToolFreehandHelper::elapsedStrokeTime ( ) const
protected

Definition at line 749 of file kis_tool_freehand_helper.cpp.

750{
751 return m_d->strokeTime.elapsed();
752}

References m_d, and KisToolFreehandHelper::Private::strokeTime.

◆ endPaint()

void KisToolFreehandHelper::endPaint ( )

There might be some timer events still pending, so we should cancel them. Use this flag for the purpose. Please note that we are not in MT here, so no mutex is needed

Definition at line 681 of file kis_tool_freehand_helper.cpp.

682{
683 if (!m_d->hasPaintAtLeastOnce) {
685 } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
686 finishStroke();
687 }
688 m_d->strokeTimeoutTimer.stop();
689
690 if(m_d->airbrushingTimer.isActive()) {
691 m_d->airbrushingTimer.stop();
692 }
693
694 if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
696 }
697
700 }
701
708 m_d->strokeInfos.clear();
709
710 // last update to complete rendering if there is still something pending
713
715 m_d->strokeId.clear();
717}
virtual void endStroke(KisStrokeId id)=0
virtual void addJob(KisStrokeId id, KisStrokeJobData *data)=0
void paintAt(int strokeInfoId, const KisPaintInformation &pi)

References KisStrokesFacade::addJob(), KisToolFreehandHelper::Private::airbrushingTimer, KisToolFreehandHelper::Private::asyncUpdateHelper, KisStrokesFacade::endStroke(), KisAsynchronousStrokeUpdateHelper::endUpdateStream(), finishStroke(), KisToolFreehandHelper::Private::hasPaintAtLeastOnce, KisToolFreehandHelper::Private::infoBuilder, KisAsynchronousStrokeUpdateHelper::isActive(), m_d, KisSmoothingOptions::NO_SMOOTHING, paintAt(), KisToolFreehandHelper::Private::previousPaintInformation, KisPaintingInformationBuilder::reset(), KisToolFreehandHelper::Private::smoothingOptions, KisSmoothingOptions::STABILIZER, stabilizerEnd(), KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokeInfos, KisToolFreehandHelper::Private::strokesFacade, and KisToolFreehandHelper::Private::strokeTimeoutTimer.

◆ finishStroke

void KisToolFreehandHelper::finishStroke ( )
privateslot

◆ getStabilizedPaintInfo()

KisPaintInformation KisToolFreehandHelper::getStabilizedPaintInfo ( const QQueue< KisPaintInformation > & queue,
const KisPaintInformation & lastPaintInfo )
private

The first point is going to be overridden by lastPaintInfo, skip it.

Definition at line 783 of file kis_tool_freehand_helper.cpp.

785{
786 KisPaintInformation result(lastPaintInfo.pos(),
787 lastPaintInfo.pressure(),
788 lastPaintInfo.xTilt(),
789 lastPaintInfo.yTilt(),
790 lastPaintInfo.rotation(),
791 lastPaintInfo.tangentialPressure(),
792 lastPaintInfo.perspective(),
794 lastPaintInfo.drawingSpeed());
795
796 result.setCanvasRotation(lastPaintInfo.canvasRotation());
797 result.setCanvasMirroredH(lastPaintInfo.canvasMirroredH());
798 result.setCanvasMirroredV(lastPaintInfo.canvasMirroredV());
799
800 if (queue.size() > 1) {
801 QQueue<KisPaintInformation>::const_iterator it = queue.constBegin();
802 QQueue<KisPaintInformation>::const_iterator end = queue.constEnd();
803
807 it++;
808 int i = 2;
809
810 if (m_d->smoothingOptions->stabilizeSensors()) {
811 while (it != end) {
812 qreal k = qreal(i - 1) / i; // coeff for uniform averaging
813 result.KisPaintInformation::mixOtherWithoutTime(k, *it);
814 it++;
815 i++;
816 }
817 } else{
818 while (it != end) {
819 qreal k = qreal(i - 1) / i; // coeff for uniform averaging
820 result.KisPaintInformation::mixOtherOnlyPosition(k, *it);
821 it++;
822 i++;
823 }
824 }
825 }
826
827 return result;
828}

References KisPaintInformation::canvasMirroredH(), KisPaintInformation::canvasMirroredV(), KisPaintInformation::canvasRotation(), KisPaintInformation::drawingSpeed(), elapsedStrokeTime(), m_d, KisPaintInformation::perspective(), KisPaintInformation::pos(), KisPaintInformation::pressure(), KisPaintInformation::rotation(), KisPaintInformation::setCanvasMirroredH(), KisPaintInformation::setCanvasMirroredV(), KisPaintInformation::setCanvasRotation(), KisToolFreehandHelper::Private::smoothingOptions, KisPaintInformation::tangentialPressure(), KisPaintInformation::xTilt(), and KisPaintInformation::yTilt().

◆ initPaint()

void KisToolFreehandHelper::initPaint ( KoPointerEvent * event,
const QPointF & pixelCoords,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade * strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0 )
Parameters
eventThe event
pixelCoordsThe position of the KoPointerEvent, in pixel coordinates.
resourceManagerThe canvas resource manager
imageThe image
currentNodeThe current node
strokesFacadeThe strokes facade
overrideNodeThe override node
boundsThe bounds

Definition at line 237 of file kis_tool_freehand_helper.cpp.

243{
244 // When using touch drawing, we only get coordinates when the finger is
245 // actually pressed down, never when the finger is in the air. That means we
246 // have to clear the distance and speed information in this case, otherwise
247 // strokes start in a state depending on where the last stroke left off,
248 // which is useless. Resetting these at least makes it predictable. This
249 // can't sensibly be done in endPaint since there can be stylus or mouse
250 // events in the meantime, whose distance and speed is also unrelated.
251 if (event->isTouchEvent()) {
252 m_d->lastCursorPos.reset(pixelCoords);
254 }
255
256 QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(pixelCoords, currentZoom());
257 m_d->strokeTime.start();
260 qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, pixelCoords, 0.0);
261
262 initPaintImpl(startAngle,
263 pi,
265 image,
266 currentNode,
267 strokesFacade,
268 overrideNode,
269 bounds);
270}
KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed, const KoCanvasResourceProvider *manager)
void initPaintImpl(qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
bool isTouchEvent() const
#define bounds(x, a, b)
qreal directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle)

References bounds, currentZoom(), KisAlgebra2D::directionBetweenPoints(), elapsedStrokeTime(), KisToolFreehandHelper::Private::infoBuilder, initPaintImpl(), KoPointerEvent::isTouchEvent(), KisToolFreehandHelper::Private::lastCursorPos, m_d, KisPaintOpUtils::PositionHistory::pushThroughHistory(), KisPaintingInformationBuilder::reset(), KisPaintOpUtils::PositionHistory::reset(), KisToolFreehandHelper::Private::resourceManager, KisPaintingInformationBuilder::startStroke(), and KisToolFreehandHelper::Private::strokeTime.

◆ initPaintImpl()

void KisToolFreehandHelper::initPaintImpl ( qreal startAngle,
const KisPaintInformation & pi,
KoCanvasResourceProvider * resourceManager,
KisImageWSP image,
KisNodeSP node,
KisStrokesFacade * strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0 )
protected

Definition at line 277 of file kis_tool_freehand_helper.cpp.

285{
286 m_d->strokesFacade = strokesFacade;
287
288 m_d->haveTangent = false;
289 m_d->previousTangent = QPointF();
290
291 m_d->hasPaintAtLeastOnce = false;
292
294
296 currentNode,
298 bounds);
299 if(overrideNode) {
300 m_d->resources->setCurrentNode(overrideNode);
301 }
302
303 const bool airbrushing = m_d->resources->needsAirbrushing();
304 const bool useSpacingUpdates = m_d->resources->needsSpacingUpdates();
305
307 startAngle,
308 useSpacingUpdates ? SPACING_UPDATE_INTERVAL : LONG_TIME,
309 airbrushing ? TIMING_UPDATE_INTERVAL : LONG_TIME,
310 0);
311 KisDistanceInformation startDist = startDistInfo.makeDistInfo();
312
314 startDist);
315
316 KisStrokeStrategy *stroke =
322
324
325 m_d->history.clear();
326 m_d->distanceHistory.clear();
327 m_d->lastDrawnPixel = QPointF(-1.0, -1.0);
328 m_d->hasLastDrawnPixel = false;
330
331 if (airbrushing) {
333 m_d->airbrushingTimer.start();
336 }
337
338 if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
340 }
341
342 // If airbrushing, paint an initial dab immediately. This is a workaround for an issue where
343 // some paintops (Dyna, Particle, Sketch) might never initialize their spacing/timing
344 // information until paintAt is called.
345 if (airbrushing) {
346 paintAt(pi);
347 }
348}
void startUpdateStream(KisStrokesFacade *strokesFacade, KisStrokeId strokeId)
The KisResourcesSnapshot class takes a snapshot of the various resources like colors and settings use...
void setCurrentNode(KisNodeSP node)
virtual KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy)=0
void stabilizerStart(KisPaintInformation firstPaintInfo)
virtual void createPainters(QVector< KisFreehandStrokeInfo * > &strokeInfos, const KisDistanceInformation &startDist)
const qreal LONG_TIME
const qreal TIMING_UPDATE_INTERVAL
const qreal SPACING_UPDATE_INTERVAL
QList< KisPaintInformation > history

References KisToolFreehandHelper::Private::airbrushingTimer, KisToolFreehandHelper::Private::asyncUpdateHelper, bounds, computeAirbrushTimerInterval(), createPainters(), KisToolFreehandHelper::Private::distanceHistory, KisToolFreehandHelper::Private::hasLastDrawnPixel, KisToolFreehandHelper::Private::hasPaintAtLeastOnce, KisToolFreehandHelper::Private::haveTangent, KisToolFreehandHelper::Private::history, KisToolFreehandHelper::Private::lastDrawnPixel, LONG_TIME, m_d, KisResourcesSnapshot::needsAirbrushing(), KisResourcesSnapshot::needsSpacingUpdates(), paintAt(), KisToolFreehandHelper::Private::pixelInLineCount, KisPaintInformation::pos(), KisResourcesSnapshot::presetNeedsAsynchronousUpdates(), KisToolFreehandHelper::Private::previousPaintInformation, KisToolFreehandHelper::Private::previousTangent, resourceManager(), KisToolFreehandHelper::Private::resources, KisResourcesSnapshot::setCurrentNode(), KisToolFreehandHelper::Private::smoothingOptions, SPACING_UPDATE_INTERVAL, KisSmoothingOptions::STABILIZER, stabilizerStart(), KisStrokesFacade::startStroke(), KisAsynchronousStrokeUpdateHelper::startUpdateStream(), KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokeInfos, KisToolFreehandHelper::Private::strokesFacade, FreehandStrokeStrategy::SupportsContinuedInterstrokeData, FreehandStrokeStrategy::SupportsTimedMergeId, TIMING_UPDATE_INTERVAL, and KisToolFreehandHelper::Private::transactionText.

◆ isRunning()

bool KisToolFreehandHelper::isRunning ( ) const

Definition at line 272 of file kis_tool_freehand_helper.cpp.

273{
274 return bool(m_d->strokeId);
275}

References m_d, and KisToolFreehandHelper::Private::strokeId.

◆ paint()

void KisToolFreehandHelper::paint ( KisPaintInformation & info)
private

Smooth the coordinates out using the history and the distance. This is a heavily modified version of an algo used in Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and https://w.atwiki.jp/sigetch_2007/pages/17.html. The main differences are:

1) It uses 'distance' instead of 'velocity', since time measurements are too unstable in realworld environment

2) There is no 'Quality' parameter, since the number of samples is calculated automatically

3) 'Tail Aggressiveness' is used for controlling the end of the stroke

4) The formula is a little bit different: 'Distance' parameter stands for $3 \Sigma$

Definition at line 472 of file kis_tool_freehand_helper.cpp.

473{
494 KisPaintInformation lastUsedPaintInformation;
495 QPointF currentPixelPos = info.pos();
496 const float PIXEL_DISTANCE_THRESHOLD = 1.7;
497
498
500 && (m_d->smoothingOptions->smoothnessDistanceMin() > 0.0
501 || m_d->smoothingOptions->smoothnessDistanceMax() > 0.0)) {
502
503 { // initialize current distance
504 QPointF prevPos;
505
506 if (!m_d->history.isEmpty()) {
507 const KisPaintInformation &prevPi = m_d->history.last();
508 prevPos = prevPi.pos();
509 } else {
510 prevPos = m_d->previousPaintInformation.pos();
511 }
512
513 qreal currentDistance = QVector2D(info.pos() - prevPos).length();
514 m_d->distanceHistory.append(currentDistance);
515 }
516
517 m_d->history.append(info);
518
519 qreal x = 0.0;
520 qreal y = 0.0;
521
522 if (m_d->history.size() > 3) {
523 const qreal sigma = m_d->effectiveSmoothnessDistance(m_d->previousPaintInformation.drawingSpeed()) / 3.0; // '3.0' for (3 * sigma) range
524
525 qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
526 qreal gaussianWeight2 = sigma * sigma;
527 qreal distanceSum = 0.0;
528 qreal scaleSum = 0.0;
529 qreal pressure = 0.0;
530 qreal baseRate = 0.0;
531
532 Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
533
534 for (int i = m_d->history.size() - 1; i >= 0; i--) {
535 qreal rate = 0.0;
536
537 const KisPaintInformation nextInfo = m_d->history.at(i);
538 double distance = m_d->distanceHistory.at(i);
539 Q_ASSERT(distance >= 0.0);
540
541 qreal pressureGrad = 0.0;
542 if (i < m_d->history.size() - 1) {
543 pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
544
545 const qreal tailAggressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
546
547 if (pressureGrad > 0.0 ) {
548 pressureGrad *= tailAggressiveness * (1.0 - nextInfo.pressure());
549 distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
550 }
551 }
552
553 if (gaussianWeight2 != 0.0) {
554 distanceSum += distance;
555 rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
556 }
557
558 if (m_d->history.size() - i == 1) {
559 baseRate = rate;
560 } else if (baseRate / rate > 100) {
561 break;
562 }
563
564 scaleSum += rate;
565 x += rate * nextInfo.pos().x();
566 y += rate * nextInfo.pos().y();
567
568 if (m_d->smoothingOptions->smoothPressure()) {
569 pressure += rate * nextInfo.pressure();
570 }
571 }
572
573 if (scaleSum != 0.0) {
574 x /= scaleSum;
575 y /= scaleSum;
576
577 if (m_d->smoothingOptions->smoothPressure()) {
578 pressure /= scaleSum;
579 }
580 }
581
582 if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
583 info.setPos(QPointF(x, y));
584 if (m_d->smoothingOptions->smoothPressure()) {
585 info.setPressure(pressure);
586 }
587 m_d->history.last() = info;
588 }
589 }
590 }
591
594 {
595 // Now paint between the coordinates, using the bezier curve interpolation
596 if (!m_d->haveTangent) {
597 m_d->haveTangent = true;
599 (info.pos() - m_d->previousPaintInformation.pos()) /
600 qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
601 } else {
602 QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
603 qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
604
605 if (newTangent.isNull() || m_d->previousTangent.isNull())
606 {
608 } else {
610 m_d->previousTangent, newTangent);
611 }
612
613 m_d->previousTangent = newTangent;
614 }
616
617 // Enable stroke timeout only when not airbrushing.
618 if (!m_d->airbrushingTimer.isActive()) {
619 m_d->strokeTimeoutTimer.start(100);
620 }
621 }
622
623 else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
625 }
626
628 // initial case: No last drawn pixel or waiting pixel
629 if(!m_d->hasLastDrawnPixel) {
630 currentPixelPos = info.pos();
631 m_d->waitingPixel = currentPixelPos;
632
635 m_d->hasLastDrawnPixel = true;
637 } else {
638 if (abs(currentPixelPos.x() - m_d->lastDrawnPixel.x()) > PIXEL_DISTANCE_THRESHOLD || abs(currentPixelPos.y() - m_d->lastDrawnPixel.y()) > PIXEL_DISTANCE_THRESHOLD) {
640 // current pixel is too far, draw the waiting pixel
642 m_d->pixelInLineCount += 2;
644 m_d->waitingPixel = currentPixelPos;
645 }
646 // check axis, if the currentpixel is in the same axis as the lastdrawnpixel, we can draw waiting pixel
647 if (m_d->pixelInLineCount >= 1 && (currentPixelPos.x() == m_d->lastDrawnPixel.x() || currentPixelPos.y() == m_d->lastDrawnPixel.y())) {
652 }
653 else{
654 //otherwise just change update waiting pixel without drawing it, this is the scenario where we just skip a corner and therefore we also reset pixelInLineCount
655 m_d->waitingPixel = currentPixelPos;
657 }
658 // Enable stroke timeout only when not airbrushing.
659 if (!m_d->airbrushingTimer.isActive()) {
660 m_d->strokeTimeoutTimer.start(100);
661 }
662 }
663 }
664
665 if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
668 // Paint here so we don't have to rely on the timer
669 // This is just a tricky source for a relatively stable 7ms "timer"
671 }
672 } else {
674 }
675
676 if(m_d->airbrushingTimer.isActive()) {
677 m_d->airbrushingTimer.start();
678 }
679}
qreal distance(const QPointF &p1, const QPointF &p2)
void setPos(const QPointF &p)
void setPressure(qreal p)
Set the pressure.
void addEvent(const KisPaintInformation &pi)
#define M_PI
Definition kis_global.h:111
Point abs(const Point &pt)
KisStabilizedEventsSampler stabilizedSampler
qreal effectiveSmoothnessDistance(qreal speed) const

References KisStabilizedEventsSampler::addEvent(), KisToolFreehandHelper::Private::airbrushingTimer, KisPaintInformation::currentTime(), distance(), KisToolFreehandHelper::Private::distanceHistory, KisPaintInformation::drawingSpeed(), KisToolFreehandHelper::Private::effectiveSmoothnessDistance(), KisToolFreehandHelper::Private::hasLastDrawnPixel, KisToolFreehandHelper::Private::haveTangent, KisToolFreehandHelper::Private::history, KisToolFreehandHelper::Private::lastDrawnPixel, m_d, M_PI, KisSmoothingOptions::NO_SMOOTHING, KisToolFreehandHelper::Private::olderPaintInformation, paintBezierSegment(), paintLine(), KisStabilizerDelayedPaintHelper::paintSome(), KisSmoothingOptions::PIXEL_PERFECT, KisToolFreehandHelper::Private::pixelInLineCount, KisPaintInformation::pos(), KisPaintInformation::pressure(), KisToolFreehandHelper::Private::previousPaintInformation, KisToolFreehandHelper::Private::previousTangent, KisStabilizerDelayedPaintHelper::running(), KisPaintInformation::setPos(), KisPaintInformation::setPressure(), KisSmoothingOptions::SIMPLE_SMOOTHING, KisToolFreehandHelper::Private::smoothingOptions, KisToolFreehandHelper::Private::stabilizedSampler, KisSmoothingOptions::STABILIZER, KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::strokeTimeoutTimer, KisToolFreehandHelper::Private::waitingPixel, and KisSmoothingOptions::WEIGHTED_SMOOTHING.

◆ paintAt() [1/2]

void KisToolFreehandHelper::paintAt ( const KisPaintInformation & pi)
protectedvirtual

Reimplemented in KisToolMultihandHelper, and KisToolMultihandHelper.

Definition at line 1048 of file kis_tool_freehand_helper.cpp.

1049{
1050 paintAt(0, pi);
1051}

References paintAt().

◆ paintAt() [2/2]

◆ paintBezierCurve() [1/2]

void KisToolFreehandHelper::paintBezierCurve ( const KisPaintInformation & pi1,
const QPointF & control1,
const QPointF & control2,
const KisPaintInformation & pi2 )
protectedvirtual

Reimplemented in KisToolMultihandHelper, and KisToolMultihandHelper.

Definition at line 1059 of file kis_tool_freehand_helper.cpp.

1063{
1064 paintBezierCurve(0, pi1, control1, control2, pi2);
1065}
void paintBezierCurve(int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)

References paintBezierCurve().

◆ paintBezierCurve() [2/2]

void KisToolFreehandHelper::paintBezierCurve ( int strokeInfoId,
const KisPaintInformation & pi1,
const QPointF & control1,
const QPointF & control2,
const KisPaintInformation & pi2 )
protected

Definition at line 1004 of file kis_tool_freehand_helper.cpp.

1009{
1010
1011#ifdef DEBUG_BEZIER_CURVES
1014
1015 tpi1 = pi1;
1016 tpi2 = pi2;
1017
1018 tpi1.setPressure(0.3);
1019 tpi2.setPressure(0.3);
1020
1021 paintLine(tpi1, tpi2);
1022
1023 tpi1.setPressure(0.6);
1024 tpi2.setPressure(0.3);
1025
1026 tpi1.setPos(pi1.pos());
1027 tpi2.setPos(control1);
1028 paintLine(tpi1, tpi2);
1029
1030 tpi1.setPos(pi2.pos());
1031 tpi2.setPos(control2);
1032 paintLine(tpi1, tpi2);
1033#endif
1034
1035 m_d->hasPaintAtLeastOnce = true;
1037 new FreehandStrokeStrategy::Data(strokeInfoId,
1038 pi1, control1, control2, pi2));
1039
1040}

References KisStrokesFacade::addJob(), KisToolFreehandHelper::Private::hasPaintAtLeastOnce, m_d, paintLine(), KisPaintInformation::pos(), KisPaintInformation::setPos(), KisPaintInformation::setPressure(), KisToolFreehandHelper::Private::strokeId, and KisToolFreehandHelper::Private::strokesFacade.

◆ paintBezierSegment()

void KisToolFreehandHelper::paintBezierSegment ( KisPaintInformation pi1,
KisPaintInformation pi2,
QPointF tangent1,
QPointF tangent2 )
private

Definition at line 355 of file kis_tool_freehand_helper.cpp.

357{
358 if (tangent1.isNull() || tangent2.isNull()) return;
359
360 const qreal maxSanePoint = 1e6;
361
362 QPointF controlTarget1;
363 QPointF controlTarget2;
364
365 // Shows the direction in which control points go
366 QPointF controlDirection1 = pi1.pos() + tangent1;
367 QPointF controlDirection2 = pi2.pos() - tangent2;
368
369 // Lines in the direction of the control points
370 QLineF line1(pi1.pos(), controlDirection1);
371 QLineF line2(pi2.pos(), controlDirection2);
372
373 // Lines to check whether the control points lay on the opposite
374 // side of the line
375 QLineF line3(controlDirection1, controlDirection2);
376 QLineF line4(pi1.pos(), pi2.pos());
377
378 QPointF intersection;
379 if (line3.intersects(line4, &intersection) == QLineF::BoundedIntersection) {
380 qreal controlLength = line4.length() / 2;
381
382 line1.setLength(controlLength);
383 line2.setLength(controlLength);
384
385 controlTarget1 = line1.p2();
386 controlTarget2 = line2.p2();
387 } else {
388 QLineF::IntersectType type = line1.intersects(line2, &intersection);
389
390 if (type == QLineF::NoIntersection ||
391 intersection.manhattanLength() > maxSanePoint) {
392
393 intersection = 0.5 * (pi1.pos() + pi2.pos());
394// dbgKrita << "WARNING: there is no intersection point "
395// << "in the basic smoothing algorithms";
396 }
397
398 controlTarget1 = intersection;
399 controlTarget2 = intersection;
400 }
401
402 // shows how near to the controlTarget the value raises
403 qreal coeff = 0.8;
404
405 qreal velocity1 = QLineF(QPointF(), tangent1).length();
406 qreal velocity2 = QLineF(QPointF(), tangent2).length();
407
408 if (velocity1 == 0.0 || velocity2 == 0.0) {
409 velocity1 = 1e-6;
410 velocity2 = 1e-6;
411 warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
412 }
413
414 qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
415
416 // the controls should not differ more than 50%
417 similarity = qMax(similarity, qreal(0.5));
418
419 // when the controls are symmetric, their size should be smaller
420 // to avoid corner-like curves
421 coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
422
423 Q_ASSERT(coeff > 0);
424
425
426 QPointF control1;
427 QPointF control2;
428
429 if (velocity1 > velocity2) {
430 control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
431 coeff *= similarity;
432 control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
433 } else {
434 control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
435 coeff *= similarity;
436 control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
437 }
438
440 control1,
441 control2,
442 pi2);
443}
#define warnKrita
Definition kis_debug.h:87
#define ppVar(var)
Definition kis_debug.h:155

References paintBezierCurve(), KisPaintInformation::pos(), ppVar, and warnKrita.

◆ paintEvent()

◆ paintLine() [1/2]

void KisToolFreehandHelper::paintLine ( const KisPaintInformation & pi1,
const KisPaintInformation & pi2 )
protectedvirtual

Reimplemented in KisToolMultihandHelper, and KisToolMultihandHelper.

Definition at line 1053 of file kis_tool_freehand_helper.cpp.

1055{
1056 paintLine(0, pi1, pi2);
1057}

References paintLine().

◆ paintLine() [2/2]

void KisToolFreehandHelper::paintLine ( int strokeInfoId,
const KisPaintInformation & pi1,
const KisPaintInformation & pi2 )
protected

◆ paintOpOutline()

KisOptimizedBrushOutline KisToolFreehandHelper::paintOpOutline ( const QPointF & savedCursorPos,
const KoPointerEvent * event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode ) const

When LoD mode is active it may happen that the helper has already started a stroke, but it painted noting, because all the work is being calculated by the scaled-down LodN stroke. So at first we try to fetch the data from the lodN stroke ("buddy") and then check if there is at least something has been painted with this distance information object.

Tiny hack alert: here we fetch the distance information directly from the LodN stroke. Ideally, we should upscale its data, but here we just override it with our local copy of the coordinates.

Definition at line 161 of file kis_tool_freehand_helper.cpp.

165{
166 KisPaintOpSettingsSP settings = globalSettings;
167 QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(savedCursorPos, currentZoom());
168 qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, savedCursorPos, 0);
169 KisDistanceInformation distanceInfo(prevPoint, startAngle);
170
172
173 if (!m_d->strokeInfos.isEmpty()) {
174 settings = m_d->resources->currentPaintOpPreset()->settings();
178 } else {
180 }
181
191 KisDistanceInformation *buddyDistance =
192 m_d->strokeInfos.first()->buddyDragDistance();
193
194 if (buddyDistance) {
201 distanceInfo = *buddyDistance;
202 distanceInfo.overrideLastValues(prevPoint, startAngle);
203
204 } else if (m_d->strokeInfos.first()->dragDistance->isStarted()) {
205 distanceInfo = *m_d->strokeInfos.first()->dragDistance;
206 }
207 } else {
208 info = m_d->infoBuilder->hover(savedCursorPos, event, !m_d->strokeInfos.isEmpty());
209 }
210
212 info.registerDistanceInformation(&distanceInfo);
213
216
217 KisOptimizedBrushOutline outline = settings->brushOutline(info, mode, currentPhysicalZoom());
218
219 if (m_d->resources &&
221 m_d->smoothingOptions->useDelayDistance()) {
222
223 const qreal R = m_d->smoothingOptions->delayDistance() /
225
226 outline.addEllipse(info.pos(), R, R);
227 }
228
229 return outline;
230}
Eigen::Matrix< double, 4, 2 > R
void addEllipse(const QPointF &center, qreal rx, qreal ry)
void setRandomSource(KisRandomSourceSP value)
DistanceInformationRegistrar registerDistanceInformation(KisDistanceInformation *distance)
void setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value)
KisPaintInformation hover(const QPointF &imagePoint, const KoPointerEvent *event, bool isStrokeStarted)
KisPaintOpPresetSP currentPaintOpPreset() const
void overrideLastValues(const QPointF &lastPosition, qreal lastAngle)

References KisOptimizedBrushOutline::addEllipse(), KisResourcesSnapshot::currentPaintOpPreset(), currentPhysicalZoom(), currentZoom(), KisAlgebra2D::directionBetweenPoints(), KisResourcesSnapshot::effectiveZoom(), KisToolFreehandHelper::Private::fakeDabRandomSource, KisToolFreehandHelper::Private::fakeStrokeRandomSource, KisStabilizerDelayedPaintHelper::hasLastPaintInformation(), KisPaintingInformationBuilder::hover(), KisToolFreehandHelper::Private::infoBuilder, KisToolFreehandHelper::Private::lastCursorPos, KisStabilizerDelayedPaintHelper::lastPaintInformation(), m_d, KisDistanceInformation::overrideLastValues(), KisPaintInformation::pos(), KisToolFreehandHelper::Private::previousPaintInformation, KisPaintOpUtils::PositionHistory::pushThroughHistory(), R, KisPaintInformation::registerDistanceInformation(), KisToolFreehandHelper::Private::resources, KisStabilizerDelayedPaintHelper::running(), KisPaintInformation::setPerStrokeRandomSource(), KisPaintInformation::setRandomSource(), KisToolFreehandHelper::Private::smoothingOptions, KisSmoothingOptions::STABILIZER, KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, and KisToolFreehandHelper::Private::strokeInfos.

◆ requestExplicitUpdateOutline

void KisToolFreehandHelper::requestExplicitUpdateOutline ( )
signal

The signal is emitted when the outline should be updated explicitly by the tool. Used by Stabilizer option, because it paints on internal timer events instead of the on every paint() event

◆ resourceManager()

KoCanvasResourceProvider * KisToolFreehandHelper::resourceManager ( ) const
protected

Definition at line 350 of file kis_tool_freehand_helper.cpp.

351{
352 return m_d->resourceManager;
353}

References m_d, and KisToolFreehandHelper::Private::resourceManager.

◆ setSmoothness()

void KisToolFreehandHelper::setSmoothness ( KisSmoothingOptionsSP smoothingOptions)

◆ slotSmoothingTypeChanged

◆ smoothingOptions()

KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions ( ) const

Definition at line 156 of file kis_tool_freehand_helper.cpp.

157{
158 return m_d->smoothingOptions;
159}

References m_d, and KisToolFreehandHelper::Private::smoothingOptions.

◆ stabilizerEnd()

void KisToolFreehandHelper::stabilizerEnd ( )
private

◆ stabilizerPollAndPaint

void KisToolFreehandHelper::stabilizerPollAndPaint ( )
privateslot

Definition at line 830 of file kis_tool_freehand_helper.cpp.

831{
834 std::tie(it, end) = m_d->stabilizedSampler.range();
835 QVector<KisPaintInformation> delayedPaintTodoItems;
836
837 for (; it != end; ++it) {
838 KisPaintInformation sampledInfo = *it;
839
840 bool canPaint = true;
841
842 if (m_d->smoothingOptions->useDelayDistance()) {
843 const qreal R = m_d->smoothingOptions->delayDistance() /
845
846 QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
847 qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
848
849 if (!(dx > R)) {
851 sampledInfo.setPos(m_d->previousPaintInformation.pos());
852 }
853 else {
854 canPaint = false;
855 }
856 }
857 }
858
859 if (canPaint) {
861
863 delayedPaintTodoItems.append(newInfo);
864 } else {
866 }
867 m_d->previousPaintInformation = newInfo;
868
869 // Push the new entry through the queue
870 m_d->stabilizerDeque.dequeue();
871 m_d->stabilizerDeque.enqueue(sampledInfo);
872 } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
873 QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
874 QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();
875
876 while (it != end) {
878 ++it;
879 }
880 }
881 }
882
884
886 m_d->stabilizerDelayedPaintHelper.update(delayedPaintTodoItems);
887 } else {
889 }
890}
std::pair< iterator, iterator > range() const
void update(const QVector< KisPaintInformation > &newPaintInfos)
KisPaintInformation getStabilizedPaintInfo(const QQueue< KisPaintInformation > &queue, const KisPaintInformation &lastPaintInfo)
T pow2(const T &x)
Definition kis_global.h:166

References KisStabilizedEventsSampler::clear(), KisResourcesSnapshot::effectiveZoom(), getStabilizedPaintInfo(), m_d, KisResourcesSnapshot::needsAirbrushing(), paintLine(), KisPaintInformation::pos(), pow2(), KisToolFreehandHelper::Private::previousPaintInformation, R, KisStabilizedEventsSampler::range(), requestExplicitUpdateOutline(), KisToolFreehandHelper::Private::resources, KisStabilizerDelayedPaintHelper::running(), KisPaintInformation::setPos(), KisToolFreehandHelper::Private::smoothingOptions, KisToolFreehandHelper::Private::stabilizedSampler, KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::stabilizerDeque, and KisStabilizerDelayedPaintHelper::update().

◆ stabilizerStart()

void KisToolFreehandHelper::stabilizerStart ( KisPaintInformation firstPaintInfo)
private

Definition at line 754 of file kis_tool_freehand_helper.cpp.

755{
756 m_d->usingStabilizer = true;
757 // FIXME: Ugly hack, this is no a "distance" in any way
758 int sampleSize = qRound(m_d->effectiveSmoothnessDistance(firstPaintInfo.drawingSpeed()));
759 sampleSize = qMax(3, sampleSize);
760
761 // Fill the deque with the current value repeated until filling the sample
762 m_d->stabilizerDeque.clear();
763 for (int i = sampleSize; i > 0; i--) {
764 m_d->stabilizerDeque.enqueue(firstPaintInfo);
765 }
766
767 // Poll and draw regularly
768 KisConfig cfg(true);
769 int stabilizerSampleSize = cfg.stabilizerSampleSize();
770 m_d->stabilizerPollTimer.setInterval(stabilizerSampleSize);
771 m_d->stabilizerPollTimer.start();
772
773 bool delayedPaintEnabled = cfg.stabilizerDelayedPaint();
774 if (delayedPaintEnabled) {
775 m_d->stabilizerDelayedPaintHelper.start(firstPaintInfo);
776 }
777
779 m_d->stabilizedSampler.addEvent(firstPaintInfo);
780}
void start(const KisPaintInformation &firstPaintInfo)

References KisStabilizedEventsSampler::addEvent(), KisStabilizedEventsSampler::clear(), KisPaintInformation::drawingSpeed(), KisToolFreehandHelper::Private::effectiveSmoothnessDistance(), m_d, KisToolFreehandHelper::Private::stabilizedSampler, KisConfig::stabilizerDelayedPaint(), KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::stabilizerDeque, KisToolFreehandHelper::Private::stabilizerPollTimer, KisConfig::stabilizerSampleSize(), KisStabilizerDelayedPaintHelper::start(), and KisToolFreehandHelper::Private::usingStabilizer.

Member Data Documentation

◆ m_d

Private* const KisToolFreehandHelper::m_d
private

Definition at line 153 of file kis_tool_freehand_helper.h.


The documentation for this class was generated from the following files: