18#include "stroketool.h"
21#include "scribblearea.h"
22#include "viewmanager.h"
23#include "preferencemanager.h"
25#include "toolmanager.h"
28#include "canvascursorpainter.h"
32 void detectWhichOSX();
33 void disableCoalescing();
34 void enableCoalescing();
38 void detectWhichOSX() {}
39 void disableCoalescing() {}
40 void enableCoalescing() {}
44const qreal StrokeTool::FEATHER_MIN = 1.;
45const qreal StrokeTool::FEATHER_MAX = 99.;
46const qreal StrokeTool::WIDTH_MIN = 1.;
47const qreal StrokeTool::WIDTH_MAX = 200.;
50bool StrokeTool::mQuickSizingEnabled =
false;
57StrokeTool::~StrokeTool()
63 mQuickSizingEnabled = mEditor->preference()->isOn(SETTING::QUICK_SIZING);
64 mCanvasCursorEnabled = mEditor->preference()->isOn(SETTING::CANVAS_CURSOR);
66 QSettings pencilSettings(PENCIL2D, PENCIL2D);
68 info[StrokeToolProperties::WIDTH_VALUE] = { WIDTH_MIN, WIDTH_MAX, 24.0 };
69 info[StrokeToolProperties::FEATHER_VALUE] = { FEATHER_MIN, FEATHER_MAX, 48.0 };
70 info[StrokeToolProperties::FEATHER_ENABLED] =
false;
71 info[StrokeToolProperties::PRESSURE_ENABLED] =
false;
72 info[StrokeToolProperties::INVISIBILITY_ENABLED] =
false;
73 info[StrokeToolProperties::STABILIZATION_VALUE] = { StabilizationLevel::NONE, StabilizationLevel::STRONG, StabilizationLevel::STRONG };
74 info[StrokeToolProperties::ANTI_ALIASING_ENABLED] =
false;
75 info[StrokeToolProperties::FILLCONTOUR_ENABLED] =
false;
77 toolProperties().insertProperties(info);
78 toolProperties().loadFrom(typeName(), pencilSettings);
82 connect(mEditor->preference(), &PreferenceManager::optionChanged,
this, &StrokeTool::onPreferenceChanged);
84 connect(&mWidthSizingTool, &RadialOffsetTool::offsetChanged,
this, [=](qreal offset) {
85 setWidth(offset * 2.0);
88 connect(&mFeatherSizingTool, &RadialOffsetTool::offsetChanged,
this, [=](qreal offset){
89 const qreal inputMin = FEATHER_MIN;
90 const qreal inputMax = strokeToolProperties().width() * 0.5;
91 const qreal outputMax = FEATHER_MAX;
92 const qreal outputMin = inputMin;
95 const qreal mappedValue = MathUtils::map(offset, inputMin, inputMax, outputMax, outputMin);
97 setFeather(mappedValue);
103 mActiveConnections.
append(
connect(mEditor->view(), &ViewManager::viewChanged,
this, &StrokeTool::onViewUpdated));
112void StrokeTool::onPreferenceChanged(SETTING setting)
114 if (setting == SETTING::QUICK_SIZING) {
115 mQuickSizingEnabled = mEditor->preference()->isOn(setting);
116 }
else if (setting == SETTING::CANVAS_CURSOR) {
117 mCanvasCursorEnabled = mEditor->preference()->isOn(setting);
121void StrokeTool::onViewUpdated()
123 updateCanvasCursor();
126QPointF StrokeTool::getCurrentPressPixel()
const
128 return mInterpolator.getCurrentPressPixel();
131QPointF StrokeTool::getCurrentPressPoint()
const
133 return mEditor->view()->mapScreenToCanvas(mInterpolator.getCurrentPressPixel());
136QPointF StrokeTool::getCurrentPixel()
const
138 return mInterpolator.getCurrentPixel();
141QPointF StrokeTool::getCurrentPoint()
const
143 return mEditor->view()->mapScreenToCanvas(getCurrentPixel());
146QPointF StrokeTool::getLastPixel()
const
148 return mInterpolator.getLastPixel();
151QPointF StrokeTool::getLastPoint()
const
153 return mEditor->view()->mapScreenToCanvas(getLastPixel());
156void StrokeTool::startStroke(PointerEvent::InputType inputType)
164 mLastPixel = getCurrentPixel();
166 mStrokePoints.
clear();
169 QPointF startStrokes = mInterpolator.interpolateStart(mLastPixel);
170 mStrokePoints << mEditor->view()->mapScreenToCanvas(startStrokes);
172 mStrokePressures.
clear();
173 mStrokePressures << mInterpolator.getPressure();
175 mCurrentInputType = inputType;
176 mUndoSaveState = mEditor->undoRedo()->
state(UndoRedoRecordType::KEYFRAME_MODIFY);
181bool StrokeTool::keyPressEvent(
QKeyEvent *event)
183 switch (
event->key()) {
185 if (mEditor->tools()->setTemporaryTool(EYEDROPPER, {},
Qt::AltModifier))
197 return BaseTool::keyPressEvent(
event);
205void StrokeTool::endStroke()
207 mInterpolator.interpolateEnd();
208 mStrokePressures << mInterpolator.getPressure();
209 mStrokePoints.
clear();
210 mStrokePressures.
clear();
214 mEditor->setModified(mEditor->currentLayerIndex(), mEditor->currentFrame());
215 mScribbleArea->endStroke();
217 mEditor->undoRedo()->
record(mUndoSaveState, typeName());
220void StrokeTool::drawStroke()
222 QPointF pixel = getCurrentPixel();
223 if (pixel != mLastPixel || !mFirstDraw)
226 QPointF startStrokes = mInterpolator.interpolateStart(getLastPixel());
227 mStrokePoints << mEditor->view()->mapScreenToCanvas(startStrokes);
228 mStrokePressures << mInterpolator.getPressure();
238 if (!mQuickSizingEnabled) {
return false; }
241 mWidthSizingTool.stopAdjusting();
242 mFeatherSizingTool.stopAdjusting();
246 StrokeToolProperties::Type setting =
static_cast<StrokeToolProperties::Type
>(mQuickSizingProperties[
event->modifiers()]);
247 if (
event->eventType() == PointerEvent::Press) {
249 case StrokeToolProperties::WIDTH_VALUE: {
250 mWidthSizingTool.setOffset(strokeToolProperties().width() * 0.5);
253 case StrokeToolProperties::FEATHER_VALUE: {
254 const qreal factor = 0.5;
255 const qreal cursorRad = strokeToolProperties().width() * factor;
258 const qreal featherWidthFactor = MathUtils::normalize(strokeToolProperties().feather(), FEATHER_MIN, FEATHER_MAX);
259 const qreal offset = (cursorRad * featherWidthFactor);
260 mFeatherSizingTool.setOffset(offset);
268 case StrokeToolProperties::WIDTH_VALUE: {
269 mWidthSizingTool.pointerEvent(
event);
272 case StrokeToolProperties::FEATHER_VALUE: {
273 mFeatherSizingTool.pointerEvent(
event);
279 updateCanvasCursor();
285 updateCanvasCursor();
290 updateCanvasCursor();
295 updateCanvasCursor();
300 mCanvasCursorEnabled = mEditor->preference()->isOn(SETTING::CANVAS_CURSOR);
304bool StrokeTool::leaveEvent(
QEvent*)
311 mCanvasCursorEnabled =
false;
312 updateCanvasCursor();
317QRectF StrokeTool::cursorRect(StrokeToolProperties::Type settingType,
const QPointF& point)
319 const qreal brushWidth = strokeToolProperties().width();
320 const qreal brushFeather = strokeToolProperties().feather();
322 const QPointF& cursorPos = point;
323 const qreal cursorRad = brushWidth * 0.5;
324 const QPointF& widthCursorTopLeft =
QPointF(cursorPos.
x() - cursorRad, cursorPos.
y() - cursorRad);
326 const QRectF widthCircleRect =
QRectF(widthCursorTopLeft,
QSizeF(brushWidth, brushWidth));
327 if (settingType == StrokeToolProperties::WIDTH_VALUE) {
328 return widthCircleRect;
329 }
else if (settingType == StrokeToolProperties::FEATHER_VALUE) {
330 const qreal featherWidthFactor = MathUtils::normalize(brushFeather, FEATHER_MIN, FEATHER_MAX);
331 QRectF featherRect =
QRectF(widthCircleRect.
center().
x() - (cursorRad * featherWidthFactor),
332 widthCircleRect.
center().
y() - (cursorRad * featherWidthFactor),
333 brushWidth * featherWidthFactor,
334 brushWidth * featherWidthFactor);
338 return featherRect.
adjusted(2, 2, -2, -2);
345void StrokeTool::updateCanvasCursor()
348 widthOptions.circleRect = cursorRect(StrokeToolProperties::WIDTH_VALUE, mWidthSizingTool.isAdjusting() ? mWidthSizingTool.offsetPoint() : getCurrentPoint());
349 widthOptions.showCursor = mCanvasCursorEnabled;
350 widthOptions.showCross =
true;
353 featherOptions.circleRect = cursorRect(StrokeToolProperties::FEATHER_VALUE, mFeatherSizingTool.isAdjusting() ? mFeatherSizingTool.offsetPoint() : getCurrentPoint());
354 featherOptions.showCursor = mCanvasCursorEnabled;
355 featherOptions.showCross =
false;
357 if (mFeatherSizingTool.isAdjusting()) {
358 widthOptions.circleRect = cursorRect(StrokeToolProperties::WIDTH_VALUE, mFeatherSizingTool.offsetPoint());
359 }
else if (mWidthSizingTool.isAdjusting()) {
360 featherOptions.circleRect = cursorRect(StrokeToolProperties::FEATHER_VALUE, mWidthSizingTool.offsetPoint());
363 mWidthCursorPainter.preparePainter(widthOptions);
364 mFeatherCursorPainter.preparePainter(featherOptions);
366 const QRect& dirtyRect = mWidthCursorPainter.dirtyRect();
374 mWidthCursorPainter.clearDirty();
377void StrokeTool::paint(
QPainter& painter,
const QRect& blitRect)
382 if (strokeToolProperties().featherEnabled()) {
383 mFeatherCursorPainter.paint(painter, blitRect);
386 mWidthCursorPainter.paint(painter, blitRect);
391void StrokeTool::setStablizationLevel(
int level)
393 toolProperties().setBaseValue(StrokeToolProperties::STABILIZATION_VALUE, level);
394 emit stabilizationLevelChanged(level);
397void StrokeTool::setFeatherEnabled(
bool enabled)
399 toolProperties().setBaseValue(StrokeToolProperties::FEATHER_ENABLED, enabled);
400 emit featherEnabledChanged(enabled);
403void StrokeTool::setFeather(qreal feather)
405 toolProperties().setBaseValue(StrokeToolProperties::FEATHER_VALUE, feather);
406 emit featherChanged(strokeToolProperties().feather());
409void StrokeTool::setWidth(qreal width)
411 toolProperties().setBaseValue(StrokeToolProperties::WIDTH_VALUE, width);
412 emit widthChanged(strokeToolProperties().width());
415void StrokeTool::setPressureEnabled(
bool enabled)
417 toolProperties().setBaseValue(StrokeToolProperties::PRESSURE_ENABLED, enabled);
418 emit pressureEnabledChanged(enabled);
421void StrokeTool::setFillContourEnabled(
bool enabled)
423 toolProperties().setBaseValue(StrokeToolProperties::FILLCONTOUR_ENABLED, enabled);
424 emit fillContourEnabledChanged(enabled);
427void StrokeTool::setAntiAliasingEnabled(
bool enabled)
429 toolProperties().setBaseValue(StrokeToolProperties::ANTI_ALIASING_ENABLED, enabled);
430 emit antiAliasingEnabledChanged(enabled);
433void StrokeTool::setStrokeInvisibleEnabled(
bool enabled)
435 toolProperties().setBaseValue(StrokeToolProperties::INVISIBILITY_ENABLED, enabled);
436 emit invisibleStrokeEnabledChanged(enabled);
void handleDrawingOnEmptyFrame()
Call this when starting to use a paint tool.
void record(const UndoSaveState *&undoState, const QString &description)
Records the given save state.
const UndoSaveState * state(UndoRedoRecordType recordType) const
Prepares and returns a save state with the given scope.
bool contains(const Key &key) const const
void append(const T &value)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual bool event(QEvent *e)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
QRect united(const QRect &rectangle) const const
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
QPointF center() const const
QRect toAlignedRect() const const