Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tool_freehand.cc
Go to the documentation of this file.
1/*
2 * kis_tool_freehand.cc - part of Krita
3 *
4 * SPDX-FileCopyrightText: 2003-2007 Boudewijn Rempt <boud@valdyas.org>
5 * SPDX-FileCopyrightText: 2004 Bart Coppens <kde@bartcoppens.be>
6 * SPDX-FileCopyrightText: 2007, 2008, 2010 Cyrille Berger <cberger@cberger.net>
7 * SPDX-FileCopyrightText: 2009 Lukáš Tvrdý <lukast.dev@gmail.com>
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12#include "kis_tool_freehand.h"
13#include <QPainter>
14#include <QRect>
15#include <QThreadPool>
16#include <QApplication>
17#include <QScreen>
18
19#include <Eigen/Core>
20
21#include <kis_icon.h>
22#include <KoPointerEvent.h>
23#include <KoViewConverter.h>
24#include <KoCanvasController.h>
25
26//pop up palette
28
29// Krita/image
30#include <kis_layer.h>
31#include <kis_paint_layer.h>
32#include <kis_painter.h>
34#include <kis_selection.h>
37
38
39// Krita/ui
41#include "kis_config.h"
42#include "kis_config_notifier.h"
43#include "kis_image_config.h"
44#include "canvas/kis_canvas2.h"
45#include "kis_cursor.h"
46#include <KisViewManager.h>
51#include "kis_tool_utils.h"
52
53using namespace std::placeholders; // For _1 placeholder
54
55
56KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, const QCursor & cursor,
57 const KUndo2MagicString &transactionText, bool useSavedSmoothing)
58 : KisToolPaint(canvas, cursor),
59 m_brushResizeCompressor(200, std::bind(&KisToolFreehand::slotDoResizeBrush, this, _1))
60{
61
64 connect(KisConfigNotifier::instance(), SIGNAL(touchPaintingChanged()),
66
69 new KisSmoothingOptions(useSavedSmoothing));
70
71 connect(m_helper, SIGNAL(requestExplicitUpdateOutline()), SLOT(explicitUpdateOutline()));
72
73 connect(qobject_cast<KisCanvas2*>(canvas)->viewManager(), SIGNAL(brushOutlineToggled()), SLOT(explicitUpdateOutline()));
74
75 KisCanvasResourceProvider *provider = qobject_cast<KisCanvas2*>(canvas)->viewManager()->canvasResourceProvider();
76
77 connect(provider, SIGNAL(sigEffectiveCompositeOpChanged()), SLOT(explicitUpdateOutline()));
78 connect(provider, SIGNAL(sigEffectiveCompositeOpChanged()), SLOT(resetCursorStyle()));
79 connect(provider, SIGNAL(sigPaintOpPresetChanged(KisPaintOpPresetSP)), SLOT(explicitUpdateOutline()));
80 connect(provider, SIGNAL(sigPaintOpPresetChanged(KisPaintOpPresetSP)), SLOT(resetCursorStyle()));
81}
82
88
94
99
101{
102 KisConfig cfg(true);
103
104 bool useSeparateEraserCursor = cfg.separateEraserCursor() && isEraser();
105
106 switch (useSeparateEraserCursor ? cfg.eraserCursorStyle() : cfg.newCursorStyle()) {
109 break;
112 break;
115 break;
118 break;
121 break;
124 break;
127 break;
130 break;
133 break;
135 default:
137 break;
138 }
139}
140
145
147{
148 delete m_helper;
149 m_helper = helper;
150}
151
153{
154 return true;
155}
156
162
163void KisToolFreehand::activate(const QSet<KoShape*> &shapes)
164{
166}
167
176
178{
179 m_helper->initPaint(event,
180 convertToPixelCoord(event),
181 image(),
182 currentNode(),
183 image().data());
184}
185
190
192{
194 bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->mouseReleaseEvent();
195 Q_UNUSED(paintOpIgnoredEvent);
196}
197
199{
200 return true;
201}
202
204{
205 // FIXME: workaround for the Duplicate Op
207
208 requestUpdateOutline(event->point, event);
209
210 NodePaintAbility paintability = nodePaintAbility();
211 // XXX: move this to KisTool and make it work properly for clone layers: for clone layers, the shape paint tools don't work either
212 if (!nodeEditable() || paintability != PAINT) {
213 if (paintability == KisToolPaint::VECTOR || paintability == KisToolPaint::CLONE){
214 KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
215 QString message = i18n("The brush tool cannot paint on this layer. Please select a paint layer or mask.");
216 kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked"));
217 }
218 else if (paintability == MYPAINTBRUSH_UNPAINTABLE) {
219 KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
220 QString message = i18n("The MyPaint Brush Engine is not available for this colorspace");
221 kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked"));
222 }
223 event->ignore();
224
225 return;
226 }
227
229
231
232 KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
233 if (canvas2) {
234 canvas2->viewManager()->disableControls();
235 }
236
237 initStroke(event);
238}
239
251
253{
254 Q_UNUSED(event);
256
257 endStroke();
258
259 if (m_assistant && static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()) {
260 static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()->endStroke();
261 }
262
263 KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
264 if (canvas2) {
265 canvas2->viewManager()->enableControls();
266 }
267
269}
270
272{
273 if (action != SampleFgNode && action != SampleFgImage) return false;
274
279 QPointF pos = adjustPosition(event->point, event->point);
280 qreal perspective = calculatePerspective(pos);
281 if (!currentPaintOpPreset()) {
282 return false;
283 }
286 event->xTilt(), event->yTilt(),
287 event->rotation(),
288 event->tangentialPressure(),
289 perspective, 0, 0);
292
293 bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->mousePressEvent(info,
294 event->modifiers(),
295 currentNode());
296 // DuplicateOP during the sampling of new source point (origin)
297 // is the only paintop that returns "false" here
298 return !paintOpIgnoredEvent;
299}
300
311
322
324{
325 if (trySampleByPaintOp(event, action)) {
327 return;
328 }
329
330 if (action != ChangeSize && action != ChangeSizeSnap) {
332 return;
333 }
334
336 m_initialGestureDocPoint = event->point;
337 m_initialGestureGlobalPoint = QCursor::pos();
338
339 m_lastDocumentPoint = event->point;
340 m_lastPaintOpSize = currentPaintOpPreset()->settings()->paintOpSize();
341
342 m_beginAlternateActionEvent = event->deepCopyEvent();
344}
345
347{
349
350 if (action != ChangeSize && action != ChangeSizeSnap) {
352 return;
353 }
354
355 QPointF lastWidgetPosition = convertDocumentToWidget(m_lastDocumentPoint);
356 QPointF actualWidgetPosition = convertDocumentToWidget(event->point);
357
358 QPointF offset = actualWidgetPosition - lastWidgetPosition;
359
360 KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
362 QRect screenRect = QGuiApplication::primaryScreen()->availableVirtualGeometry();
363
364 qreal scaleX = 0;
365 qreal scaleY = 0;
366 canvas2->coordinatesConverter()->imageScale(&scaleX, &scaleY);
367
368 const qreal maxBrushSize = KisImageConfig(true).maxBrushSize();
369 const qreal effectiveMaxDragSize = 0.5 * screenRect.width();
370 const qreal effectiveMaxBrushSize = qMin(maxBrushSize, effectiveMaxDragSize / scaleX);
371
372 const qreal scaleCoeff = effectiveMaxBrushSize / effectiveMaxDragSize;
373 const qreal sizeDiff = scaleCoeff * offset.x() ;
374
375 if (qAbs(sizeDiff) > 0.01) {
376 KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings();
377
378 qreal newSize = m_lastPaintOpSize + sizeDiff;
379
380 if (action == ChangeSizeSnap) {
381 newSize = qMax(qRound(newSize), 1);
382 }
383
384 newSize = qBound(0.01, newSize, maxBrushSize);
385
386 settings->setPaintOpSize(newSize);
387
390 m_beginAlternateActionEvent.has_value() ? &m_beginAlternateActionEvent->event : nullptr);
391 //m_brushResizeCompressor.start(newSize);
392
393 m_lastDocumentPoint = event->point;
394 m_lastPaintOpSize = newSize;
395 }
396}
397
417
419{
420 return false;
421}
422
424{
425 m_assistant = assistant;
426}
427
429{
430 m_only_one_assistant = assistant;
431}
432
434{
435 m_eraser_snapping = assistant;
436}
437
439{
440 KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings();
441
442 settings->setPaintOpSize(newSize);
444
445}
446
447QPointF KisToolFreehand::adjustPosition(const QPointF& point, const QPointF& strokeBegin)
448{
449 if (m_assistant && static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()) {
450 KisCanvas2* c = static_cast<KisCanvas2*>(canvas());
453 QPointF ap = c->paintingAssistantsDecoration()->adjustPosition(point, strokeBegin);
454 QPointF fp = (1.0 - m_magnetism) * point + m_magnetism * ap;
455 // Report the final position back to the assistant so the guides
456 // can follow the brush
458 return fp;
459 }
460 return point;
461}
462
463qreal KisToolFreehand::calculatePerspective(const QPointF &documentPoint)
464{
465 qreal perspective = 1.0;
466 Q_FOREACH (const KisPaintingAssistantSP assistant, static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()->assistants()) {
467 QPointer<KisAbstractPerspectiveGrid> grid = dynamic_cast<KisAbstractPerspectiveGrid*>(assistant.data());
468 if (grid && grid->isActive() && grid->contains(documentPoint)) {
469 perspective = grid->distance(documentPoint);
470 break;
471 }
472 }
473 return perspective;
474}
475
480
485
487 const KoPointerEvent *event,
489{
491 return m_helper->paintOpOutline(convertToPixelCoord(documentPos),
492 event,
493 currentPaintOpPreset()->settings(),
494 outlineMode);
495 else
497}
498
499
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisCoordinatesConverter * coordinatesConverter
KisViewManager * viewManager() const
KisPaintingAssistantsDecorationSP paintingAssistantsDecoration() const
static KisConfigNotifier * instance()
CursorStyle newCursorStyle(bool defaultValue=false) const
CursorStyle eraserCursorStyle(bool defaultValue=false) const
bool separateEraserCursor(bool defaultValue=false) const
void imageScale(qreal *scaleX, qreal *scaleY) const
static QCursor pixelBlackCursor()
Definition kis_cursor.cc:44
static QCursor blankCursor()
Definition kis_cursor.cc:89
static QCursor crossCursor()
Definition kis_cursor.cc:34
static QCursor triangleRightHandedCursor()
static QCursor arrowCursor()
Definition kis_cursor.cc:24
static QCursor triangleLeftHandedCursor()
static QCursor roundCursor()
Definition kis_cursor.cc:39
static QCursor pixelWhiteCursor()
Definition kis_cursor.cc:49
static QCursor eraserCursor()
int maxBrushSize(bool defaultValue=false) const
void setRandomSource(KisRandomSourceSP value)
void setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value)
void setOnlyOneAssistantSnap(bool assistant)
sets whether we snap to only one assistant
void setEraserSnap(bool assistant)
sets whether eraser brushes snap
QPointF adjustPosition(const QPointF &point, const QPointF &strokeBegin)
KisSmoothingOptionsSP smoothingOptions() const
void cursorMoved(const QPointF &cursorPos)
void paintEvent(KoPointerEvent *event)
KisOptimizedBrushOutline paintOpOutline(const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettingsSP globalSettings, KisPaintOpSettings::OutlineMode mode) const
void initPaint(KoPointerEvent *event, const QPointF &pixelCoords, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
virtual void endStroke()
KisPaintingInformationBuilder * m_infoBuilder
void setSnapEraser(bool assistant)
void deactivate() override
KisOptimizedBrushOutline getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode) override
void continuePrimaryAction(KoPointerEvent *event) override
KisToolFreehandHelper * m_helper
void mouseMoveEvent(KoPointerEvent *event) override
friend class KisToolFreehandPaintingInformationBuilder
void activateAlternateAction(AlternateAction action) override
QPoint m_initialGestureGlobalPoint
void activate(const QSet< KoShape * > &shapes) override
KisToolFreehand(KoCanvasBase *canvas, const QCursor &cursor, const KUndo2MagicString &transactionText, bool useSavedSmoothing=true)
void setOnlyOneAssistantSnap(bool assistant)
bool wantsAutoScroll() const override
void deactivateAlternateAction(AlternateAction action) override
~KisToolFreehand() override
void slotDoResizeBrush(qreal newSize)
bool primaryActionSupportsHiResEvents() const override
void setAssistant(bool assistant)
virtual void initStroke(KoPointerEvent *event)
void updateMaskSyntheticEventsFromTouch()
KisPaintingInformationBuilder * paintingInformationBuilder() const
void beginPrimaryAction(KoPointerEvent *event) override
virtual void doStroke(KoPointerEvent *event)
void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override
KisSmoothingOptionsSP smoothingOptions() const
void endAlternateAction(KoPointerEvent *event, AlternateAction action) override
void resetHelper(KisToolFreehandHelper *helper)
QPointF m_initialGestureDocPoint
void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override
std::optional< KoPointerEventWrapper > m_beginAlternateActionEvent
void resetCursorStyle() override
void endPrimaryAction(KoPointerEvent *event) override
bool supportsPaintingAssistants() const override
int flags() const override
bool trySampleByPaintOp(KoPointerEvent *event, AlternateAction action)
qreal calculatePerspective(const QPointF &documentPoint)
QPointF adjustPosition(const QPointF &point, const QPointF &strokeBegin)
void setSupportOutline(bool supportOutline)
QPointF m_outlineDocPoint
virtual void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event)
void deactivate() override
void activate(const QSet< KoShape * > &shapes) override
void deactivateAlternateAction(AlternateAction action) override
void setOutlineVisible(bool visible)
void setMode(ToolMode mode) override
bool isEraser() const
void endAlternateAction(KoPointerEvent *event, AlternateAction action) override
void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override
void activateAlternateAction(AlternateAction action) override
void mouseMoveEvent(KoPointerEvent *event) override
void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override
void enableControls()
disable and enable toolbar controls. used for disabling them during painting.
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
QPointer< KoCanvasResourceProvider > resourceManager
qreal pressure() const
qreal rotation() const
return the rotation (or a default value)
qreal yTilt() const
qreal tangentialPressure() const
Qt::KeyboardModifiers modifiers() const
QPointF point
The point in document coordinates.
qreal xTilt() const
void setMaskSyntheticEvents(bool value)
void useCursor(const QCursor &cursor)
QAction * action(const QString &name) const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
@ CURSOR_STYLE_POINTER
Definition kis_global.h:65
@ CURSOR_STYLE_SMALL_ROUND
Definition kis_global.h:66
@ CURSOR_STYLE_CROSSHAIR
Definition kis_global.h:67
@ CURSOR_STYLE_TOOLICON
Definition kis_global.h:64
@ CURSOR_STYLE_TRIANGLE_RIGHTHANDED
Definition kis_global.h:68
@ CURSOR_STYLE_WHITE_PIXEL
Definition kis_global.h:71
@ CURSOR_STYLE_BLACK_PIXEL
Definition kis_global.h:70
@ CURSOR_STYLE_TRIANGLE_LEFTHANDED
Definition kis_global.h:69
@ CURSOR_STYLE_NO_CURSOR
Definition kis_global.h:63
@ CURSOR_STYLE_ERASER
Definition kis_global.h:72
#define koIcon(name)
Use these macros for icons without any issues.
Definition kis_icon.h:25
#define CHECK_MODE_SANITY_OR_RETURN(_mode)
Definition kis_tool.h:27
void KRITAUI_EXPORT setCursorPos(const QPoint &point)
QPointF convertToPixelCoord(KoPointerEvent *e)
Definition kis_tool.cc:189
virtual ToolMode mode() const
Definition kis_tool.cc:407
KisTool::NodePaintAbility nodePaintAbility()
Definition kis_tool.cc:539
virtual void resetCursorStyle()
Definition kis_tool.cc:613
bool nodeEditable()
Checks checks if the current node is editable.
Definition kis_tool.cc:651
KisPaintOpPresetSP currentPaintOpPreset()
Definition kis_tool.cc:359
KisNodeSP currentNode() const
Definition kis_tool.cc:370
@ FLAG_USES_CUSTOM_SIZE
Definition kis_tool.h:47
@ FLAG_USES_CUSTOM_COMPOSITEOP
Definition kis_tool.h:47
@ FLAG_USES_CUSTOM_PRESET
Definition kis_tool.h:47
KisImageWSP image() const
Definition kis_tool.cc:332
@ GESTURE_MODE
Definition kis_tool.h:303
@ PAINT_MODE
Definition kis_tool.h:300
@ HOVER_MODE
Definition kis_tool.h:299
QPointF convertDocumentToWidget(const QPointF &pt)
Definition kis_tool.cc:181
AlternateAction
Definition kis_tool.h:134
@ SampleFgImage
Definition kis_tool.h:139
@ SampleFgNode
Definition kis_tool.h:137
@ ChangeSizeSnap
Definition kis_tool.h:136
@ ChangeSize
Definition kis_tool.h:135
NodePaintAbility
Definition kis_tool.h:148
@ MYPAINTBRUSH_UNPAINTABLE
Definition kis_tool.h:153
KisCanvas2 * canvas