Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • tool
brushtool.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#include "brushtool.h"
19
20#include <cmath>
21#include <QSettings>
22#include <QPixmap>
23#include <QPainter>
24#include <QColor>
25
26#include "beziercurve.h"
27#include "vectorimage.h"
28#include "editor.h"
29#include "colormanager.h"
30#include "layermanager.h"
31#include "viewmanager.h"
32#include "selectionmanager.h"
33#include "undoredomanager.h"
34#include "scribblearea.h"
35#include "pointerevent.h"
36
37
38BrushTool::BrushTool(QObject* parent) : StrokeTool(parent)
39{
40}
41
42ToolType BrushTool::type() const
43{
44 return BRUSH;
45}
46
47void BrushTool::loadSettings()
48{
49 StrokeTool::loadSettings();
50
51 mPropertyUsed[StrokeToolProperties::WIDTH_VALUE] = { Layer::BITMAP, Layer::VECTOR };
52 mPropertyUsed[StrokeToolProperties::FEATHER_VALUE] = { Layer::BITMAP };
53 mPropertyUsed[StrokeToolProperties::PRESSURE_ENABLED] = { Layer::BITMAP, Layer::VECTOR };
54 mPropertyUsed[StrokeToolProperties::INVISIBILITY_ENABLED] = { Layer::VECTOR };
55 mPropertyUsed[StrokeToolProperties::STABILIZATION_VALUE] = { Layer::BITMAP, Layer::VECTOR };
56
57 QSettings pencilSettings(PENCIL2D, PENCIL2D);
58
59 QHash<int, PropertyInfo> info;
60 info[StrokeToolProperties::WIDTH_VALUE] = { WIDTH_MIN, WIDTH_MAX, 24.0 };
61 info[StrokeToolProperties::FEATHER_VALUE] = { FEATHER_MIN, FEATHER_MAX, 48.0 };
62 info[StrokeToolProperties::FEATHER_ENABLED] = true;
63 info[StrokeToolProperties::PRESSURE_ENABLED] = true;
64 info[StrokeToolProperties::INVISIBILITY_ENABLED] = false;
65 info[StrokeToolProperties::STABILIZATION_VALUE] = { StabilizationLevel::NONE, StabilizationLevel::STRONG, StabilizationLevel::STRONG } ;
66
67 toolProperties().insertProperties(info);
68 toolProperties().loadFrom(typeName(), pencilSettings);
69
70 if (toolProperties().requireMigration(pencilSettings, ToolProperties::VERSION_1)) {
71 toolProperties().setBaseValue(StrokeToolProperties::WIDTH_VALUE, pencilSettings.value("brushWidth", 24.0).toReal());
72 toolProperties().setBaseValue(StrokeToolProperties::FEATHER_VALUE, pencilSettings.value("brushFeather", 48.0).toReal());
73 toolProperties().setBaseValue(StrokeToolProperties::PRESSURE_ENABLED, pencilSettings.value("brushPressure", true).toBool());
74 toolProperties().setBaseValue(StrokeToolProperties::INVISIBILITY_ENABLED, pencilSettings.value("brushInvisibility", false).toBool());
75 toolProperties().setBaseValue(StrokeToolProperties::STABILIZATION_VALUE, pencilSettings.value("brushLineStabilization", StabilizationLevel::STRONG).toInt());
76
77 pencilSettings.remove("brushWidth");
78 pencilSettings.remove("brushFeather");
79 pencilSettings.remove("brushPressure");
80 pencilSettings.remove("brushInvisibility");
81 pencilSettings.remove("brushLineStabilization");
82 }
83
84 mQuickSizingProperties.insert(Qt::ShiftModifier, StrokeToolProperties::WIDTH_VALUE);
85 mQuickSizingProperties.insert(Qt::ControlModifier, StrokeToolProperties::FEATHER_VALUE);
86}
87
88QCursor BrushTool::cursor()
89{
90 if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
91 {
92 return QCursor(QPixmap(":icons/general/cursor-brush.svg"), 4, 14);
93 }
94 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
95}
96
97void BrushTool::pointerPressEvent(PointerEvent *event)
98{
99 mInterpolator.pointerPressEvent(event);
100 if (handleQuickSizing(event)) {
101 return;
102 }
103
104 mMouseDownPoint = getCurrentPoint();
105 mLastBrushPoint = getCurrentPoint();
106
107 startStroke(event->inputType());
108
109 StrokeTool::pointerPressEvent(event);
110}
111
112void BrushTool::pointerMoveEvent(PointerEvent* event)
113{
114 mInterpolator.pointerMoveEvent(event);
115 if (handleQuickSizing(event)) {
116 return;
117 }
118
119 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
120 {
121 mCurrentPressure = mInterpolator.getPressure();
122 drawStroke();
123 if (mSettings.stabilizerLevel() != mInterpolator.getStabilizerLevel())
124 {
125 mInterpolator.setStabilizerLevel(mSettings.stabilizerLevel());
126 }
127 }
128
129 StrokeTool::pointerMoveEvent(event);
130}
131
132void BrushTool::pointerReleaseEvent(PointerEvent *event)
133{
134 mInterpolator.pointerReleaseEvent(event);
135 if (handleQuickSizing(event)) {
136 return;
137 }
138
139 if (event->inputType() != mCurrentInputType) return;
140
141 Layer* layer = mEditor->layers()->currentLayer();
142 mEditor->backup(typeName());
143
144 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
145 if (distance < 1)
146 {
147 paintAt(mMouseDownPoint);
148 }
149 else
150 {
151 drawStroke();
152 }
153
154 if (layer->type() == Layer::VECTOR) {
155 paintVectorStroke(layer);
156 }
157
158 endStroke();
159
160 StrokeTool::pointerReleaseEvent(event);
161}
162
163// draw a single paint dab at the given location
164void BrushTool::paintAt(QPointF point)
165{
166 //qDebug() << "Made a single dab at " << point;
167 Layer* layer = mEditor->layers()->currentLayer();
168 if (layer->type() == Layer::BITMAP)
169 {
170 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
171 qreal opacity = (mSettings.pressureEnabled()) ? (mCurrentPressure * 0.5) : 1.0;
172 qreal brushWidth = mSettings.width() * pressure;
173 mCurrentWidth = brushWidth;
174 mScribbleArea->drawBrush(point,
175 brushWidth,
176 mSettings.feather(),
177 mEditor->color()->frontColor(),
178 QPainter::CompositionMode_SourceOver,
179 opacity,
180 true);
181 }
182}
183
184void BrushTool::drawStroke()
185{
186 StrokeTool::drawStroke();
187 QList<QPointF> p = mInterpolator.interpolateStroke();
188
189 Layer* layer = mEditor->layers()->currentLayer();
190
191 if (layer->type() == Layer::BITMAP)
192 {
193 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
194 qreal opacity = (mSettings.pressureEnabled()) ? (mCurrentPressure * 0.5) : 1.0;
195 qreal brushWidth = mSettings.width() * pressure;
196 mCurrentWidth = brushWidth;
197
198 qreal brushStep = (0.5 * brushWidth);
199 brushStep = qMax(1.0, brushStep);
200
201 QPointF a = mLastBrushPoint;
202 QPointF b = getCurrentPoint();
203
204 qreal distance = 4 * QLineF(b, a).length();
205 int steps = qRound(distance / brushStep);
206
207 for (int i = 0; i < steps; i++)
208 {
209 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
210
211 mScribbleArea->drawBrush(point,
212 brushWidth,
213 mSettings.feather(),
214 mEditor->color()->frontColor(),
215 QPainter::CompositionMode_SourceOver,
216 opacity,
217 true);
218 if (i == (steps - 1))
219 {
220 mLastBrushPoint = getCurrentPoint();
221 }
222 }
223
224 // Line visualizer
225 // for debugging
226// QPainterPath tempPath;
227
228// QPointF mappedMousePos = mEditor->view()->mapScreenToCanvas(strokeManager()->getMousePos());
229// tempPath.moveTo(getCurrentPoint());
230// tempPath.lineTo(mappedMousePos);
231
232// QPen pen( Qt::black,
233// 1,
234// Qt::SolidLine,
235// Qt::RoundCap,
236// Qt::RoundJoin );
237// mScribbleArea->drawPolyline(tempPath, pen, true);
238
239 }
240 else if (layer->type() == Layer::VECTOR)
241 {
242 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1;
243 qreal brushWidth = mSettings.width() * pressure;
244
245 QPen pen(mEditor->color()->frontColor(),
246 brushWidth,
247 Qt::SolidLine,
248 Qt::RoundCap,
249 Qt::RoundJoin);
250
251 if (p.size() == 4)
252 {
253 QPainterPath path(p[0]);
254 path.cubicTo(p[1], p[2], p[3]);
255
256 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
257 }
258 }
259}
260
261// This function uses the points from DrawStroke
262// and turns them into vector lines.
263void BrushTool::paintVectorStroke(Layer* layer)
264{
265 if (mStrokePoints.empty())
266 return;
267
268 if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1)
269 {
270 // Clear the temporary pixel path
271 mScribbleArea->clearDrawingBuffer();
272 qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
273
274 BezierCurve curve(mStrokePoints, mStrokePressures, tol);
275 curve.setWidth(mSettings.width());
276 curve.setFeather(mSettings.feather());
277 curve.setFilled(false);
278 curve.setInvisibility(mSettings.invisibilityEnabled());
279 curve.setVariableWidth(mSettings.pressureEnabled());
280 curve.setColorNumber(mEditor->color()->frontColorNumber());
281
282 VectorImage* vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
283 vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
284
285 if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
286 {
287 mEditor->deselectAll();
288 }
289
290 vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
291
292 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
293 }
294}
BezierCurve
Definition: beziercurve.h:34
ColorManager::frontColor
QColor frontColor(bool useIndexedColor=true)
frontColor
Definition: colormanager.cpp:61
Layer
Definition: layer.h:33
PointerEvent
Definition: pointerevent.h:8
StrokeTool
Definition: stroketool.h:35
StrokeTool::loadSettings
void loadSettings() override
Definition: stroketool.cpp:61
VectorImage
Definition: vectorimage.h:32
VectorImage::setSelected
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.
Definition: vectorimage.cpp:616
VectorImage::getLastCurveNumber
int getLastCurveNumber()
VectorImage::getLastCurveNumber.
Definition: vectorimage.cpp:2264
VectorImage::addCurve
void addCurve(BezierCurve &newCurve, qreal factor, bool interacts=true)
VectorImage::addCurve.
Definition: vectorimage.cpp:341
VectorImage::isAnyCurveSelected
bool isAnyCurveSelected()
VectorImage::isAnyCurveSelected.
Definition: vectorimage.cpp:826
QCursor
QHash
QHash::insert
QHash::iterator insert(const Key &key, const T &value)
QLineF
QLineF::length
qreal length() const const
QList
QList::empty
bool empty() const const
QList::size
int size() const const
QObject
QObject::event
virtual bool event(QEvent *e)
QPainter::CompositionMode_SourceOver
CompositionMode_SourceOver
QPainterPath
QPen
QPixmap
QPointF
QSettings
QSettings::remove
void remove(const QString &key)
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
Qt::NoBrush
NoBrush
Qt::ShiftModifier
ShiftModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::SolidLine
SolidLine
QVariant::toBool
bool toBool() const const
QVariant::toInt
int toInt(bool *ok) const const
QVariant::toReal
qreal toReal(bool *ok) const const
Generated on Wed Dec 24 2025 23:46:02 for Pencil2D by doxygen 1.9.6 based on revision 1be63043db1f7fc0545528fc3b6d5098a552755b