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()
43{
44 return BRUSH;
45}
46
47void BrushTool::loadSettings()
48{
49 StrokeTool::loadSettings();
50
51 mPropertyEnabled[WIDTH] = true;
52 mPropertyEnabled[FEATHER] = true;
53 mPropertyEnabled[PRESSURE] = true;
54 mPropertyEnabled[INVISIBILITY] = true;
55 mPropertyEnabled[STABILIZATION] = true;
56
57 QSettings settings(PENCIL2D, PENCIL2D);
58
59 properties.width = settings.value("brushWidth", 24.0).toDouble();
60 properties.feather = settings.value("brushFeather", 48.0).toDouble();
61 properties.pressure = settings.value("brushPressure", true).toBool();
62 properties.invisibility = settings.value("brushInvisibility", false).toBool();
63 properties.preserveAlpha = OFF;
64 properties.stabilizerLevel = settings.value("brushLineStabilization", StabilizationLevel::STRONG).toInt();
65 properties.useAA = DISABLED;
66
67 if (properties.width <= 0) { setWidth(15); }
68 if (std::isnan(properties.feather)) { setFeather(15); }
69
70 mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
71 mQuickSizingProperties.insert(Qt::ControlModifier, FEATHER);
72}
73
74void BrushTool::saveSettings()
75{
76 QSettings settings(PENCIL2D, PENCIL2D);
77
78 settings.setValue("brushWidth", properties.width);
79 settings.setValue("brushFeather", properties.feather);
80 settings.setValue("brushPressure", properties.pressure);
81 settings.setValue("brushInvisibility", properties.invisibility);
82 settings.setValue("brushLineStabilization", properties.stabilizerLevel);
83
84 settings.sync();
85}
86
87void BrushTool::resetToDefault()
88{
89 setWidth(24.0);
90 setFeather(48.0);
91 setStabilizerLevel(StabilizationLevel::STRONG);
92}
93
94void BrushTool::setWidth(const qreal width)
95{
96 // Set current property
97 properties.width = width;
98}
99
100void BrushTool::setFeather(const qreal feather)
101{
102 // Set current property
103 properties.feather = feather;
104}
105
106void BrushTool::setInvisibility(const bool invisibility)
107{
108 // force value
109 properties.invisibility = invisibility;
110}
111
112void BrushTool::setPressure(const bool pressure)
113{
114 // Set current property
115 properties.pressure = pressure;
116}
117
118void BrushTool::setStabilizerLevel(const int level)
119{
120 properties.stabilizerLevel = level;
121}
122
123QCursor BrushTool::cursor()
124{
125 if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
126 {
127 return QCursor(QPixmap(":icons/general/cursor-brush.svg"), 4, 14);
128 }
129 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
130}
131
132void BrushTool::pointerPressEvent(PointerEvent *event)
133{
134 mInterpolator.pointerPressEvent(event);
135 if (handleQuickSizing(event)) {
136 return;
137 }
138
139 mMouseDownPoint = getCurrentPoint();
140 mLastBrushPoint = getCurrentPoint();
141
142 startStroke(event->inputType());
143
144 StrokeTool::pointerPressEvent(event);
145}
146
147void BrushTool::pointerMoveEvent(PointerEvent* event)
148{
149 mInterpolator.pointerMoveEvent(event);
150 if (handleQuickSizing(event)) {
151 return;
152 }
153
154 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
155 {
156 mCurrentPressure = mInterpolator.getPressure();
157 drawStroke();
158 if (properties.stabilizerLevel != mInterpolator.getStabilizerLevel())
159 {
160 mInterpolator.setStabilizerLevel(properties.stabilizerLevel);
161 }
162 }
163
164 StrokeTool::pointerMoveEvent(event);
165}
166
167void BrushTool::pointerReleaseEvent(PointerEvent *event)
168{
169 mInterpolator.pointerReleaseEvent(event);
170 if (handleQuickSizing(event)) {
171 return;
172 }
173
174 if (event->inputType() != mCurrentInputType) return;
175
176 Layer* layer = mEditor->layers()->currentLayer();
177 mEditor->backup(typeName());
178
179 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
180 if (distance < 1)
181 {
182 paintAt(mMouseDownPoint);
183 }
184 else
185 {
186 drawStroke();
187 }
188
189 if (layer->type() == Layer::VECTOR) {
190 paintVectorStroke(layer);
191 }
192
193 endStroke();
194
195 StrokeTool::pointerReleaseEvent(event);
196}
197
198// draw a single paint dab at the given location
199void BrushTool::paintAt(QPointF point)
200{
201 //qDebug() << "Made a single dab at " << point;
202 Layer* layer = mEditor->layers()->currentLayer();
203 if (layer->type() == Layer::BITMAP)
204 {
205 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
206 qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
207 qreal brushWidth = properties.width * pressure;
208 mCurrentWidth = brushWidth;
209 mScribbleArea->drawBrush(point,
210 brushWidth,
211 properties.feather,
212 mEditor->color()->frontColor(),
213 QPainter::CompositionMode_SourceOver,
214 opacity,
215 true);
216 }
217}
218
219void BrushTool::drawStroke()
220{
221 StrokeTool::drawStroke();
222 QList<QPointF> p = mInterpolator.interpolateStroke();
223
224 Layer* layer = mEditor->layers()->currentLayer();
225
226 if (layer->type() == Layer::BITMAP)
227 {
228 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
229 qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
230 qreal brushWidth = properties.width * pressure;
231 mCurrentWidth = brushWidth;
232
233 qreal brushStep = (0.5 * brushWidth);
234 brushStep = qMax(1.0, brushStep);
235
236 QPointF a = mLastBrushPoint;
237 QPointF b = getCurrentPoint();
238
239 qreal distance = 4 * QLineF(b, a).length();
240 int steps = qRound(distance / brushStep);
241
242 for (int i = 0; i < steps; i++)
243 {
244 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
245
246 mScribbleArea->drawBrush(point,
247 brushWidth,
248 properties.feather,
249 mEditor->color()->frontColor(),
250 QPainter::CompositionMode_SourceOver,
251 opacity,
252 true);
253 if (i == (steps - 1))
254 {
255 mLastBrushPoint = getCurrentPoint();
256 }
257 }
258
259 // Line visualizer
260 // for debugging
261// QPainterPath tempPath;
262
263// QPointF mappedMousePos = mEditor->view()->mapScreenToCanvas(strokeManager()->getMousePos());
264// tempPath.moveTo(getCurrentPoint());
265// tempPath.lineTo(mappedMousePos);
266
267// QPen pen( Qt::black,
268// 1,
269// Qt::SolidLine,
270// Qt::RoundCap,
271// Qt::RoundJoin );
272// mScribbleArea->drawPolyline(tempPath, pen, true);
273
274 }
275 else if (layer->type() == Layer::VECTOR)
276 {
277 qreal pressure = (properties.pressure) ? mCurrentPressure : 1;
278 qreal brushWidth = properties.width * pressure;
279
280 QPen pen(mEditor->color()->frontColor(),
281 brushWidth,
282 Qt::SolidLine,
283 Qt::RoundCap,
284 Qt::RoundJoin);
285
286 if (p.size() == 4)
287 {
288 QPainterPath path(p[0]);
289 path.cubicTo(p[1], p[2], p[3]);
290
291 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
292 }
293 }
294}
295
296// This function uses the points from DrawStroke
297// and turns them into vector lines.
298void BrushTool::paintVectorStroke(Layer* layer)
299{
300 if (mStrokePoints.empty())
301 return;
302
303 if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1)
304 {
305 // Clear the temporary pixel path
306 mScribbleArea->clearDrawingBuffer();
307 qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
308
309 BezierCurve curve(mStrokePoints, mStrokePressures, tol);
310 curve.setWidth(properties.width);
311 curve.setFeather(properties.feather);
312 curve.setFilled(false);
313 curve.setInvisibility(properties.invisibility);
314 curve.setVariableWidth(properties.pressure);
315 curve.setColorNumber(mEditor->color()->frontColorNumber());
316
317 VectorImage* vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
318 vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
319
320 if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
321 {
322 mEditor->deselectAll();
323 }
324
325 vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
326
327 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
328 }
329}
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:34
StrokeTool::loadSettings
void loadSettings() override
Definition: stroketool.cpp:58
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::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
Qt::NoBrush
NoBrush
Qt::ShiftModifier
ShiftModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::SolidLine
SolidLine
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39