1 /*
2
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU 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 "layervector.h"
29 #include "editor.h"
30 #include "colormanager.h"
31 #include "strokemanager.h"
32 #include "layermanager.h"
33 #include "viewmanager.h"
34 #include "selectionmanager.h"
35 #include "scribblearea.h"
36 #include "blitrect.h"
37 #include "pointerevent.h"
38
39
BrushTool(QObject * parent)40 BrushTool::BrushTool(QObject* parent) : StrokeTool(parent)
41 {
42 }
43
type()44 ToolType BrushTool::type()
45 {
46 return BRUSH;
47 }
48
loadSettings()49 void BrushTool::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
resetToDefault()74 void BrushTool::resetToDefault()
75 {
76 setWidth(24.0);
77 setFeather(48.0);
78 setStabilizerLevel(StabilizationLevel::STRONG);
79 }
80
setWidth(const qreal width)81 void BrushTool::setWidth(const qreal width)
82 {
83 // Set current property
84 properties.width = width;
85
86 // Update settings
87 QSettings settings(PENCIL2D, PENCIL2D);
88 settings.setValue("brushWidth", width);
89 settings.sync();
90 }
91
setFeather(const qreal feather)92 void BrushTool::setFeather(const qreal feather)
93 {
94 // Set current property
95 properties.feather = feather;
96
97 // Update settings
98 QSettings settings(PENCIL2D, PENCIL2D);
99 settings.setValue("brushFeather", feather);
100 settings.sync();
101 }
102
setInvisibility(const bool invisibility)103 void BrushTool::setInvisibility(const bool invisibility)
104 {
105 // force value
106 properties.invisibility = invisibility;
107
108 QSettings settings(PENCIL2D, PENCIL2D);
109 settings.setValue("brushInvisibility", invisibility);
110 settings.sync();
111 }
112
setPressure(const bool pressure)113 void BrushTool::setPressure(const bool pressure)
114 {
115 // Set current property
116 properties.pressure = pressure;
117
118 // Update settings
119 QSettings settings(PENCIL2D, PENCIL2D);
120 settings.setValue("brushPressure", pressure);
121 settings.sync();
122 }
123
setStabilizerLevel(const int level)124 void BrushTool::setStabilizerLevel(const int level)
125 {
126 properties.stabilizerLevel = level;
127
128 QSettings settings(PENCIL2D, PENCIL2D);
129 settings.setValue("brushLineStabilization", level);
130 settings.sync();
131 }
132
cursor()133 QCursor BrushTool::cursor()
134 {
135 if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
136 {
137 return QCursor(QPixmap(":icons/brush.png"), 0, 13);
138 }
139 return QCursor(QPixmap(":icons/cross.png"), 10, 10);
140 }
141
pointerPressEvent(PointerEvent * event)142 void BrushTool::pointerPressEvent(PointerEvent *event)
143 {
144 mMouseDownPoint = getCurrentPoint();
145 mLastBrushPoint = getCurrentPoint();
146
147 startStroke(event->inputType());
148 }
149
pointerMoveEvent(PointerEvent * event)150 void BrushTool::pointerMoveEvent(PointerEvent* event)
151 {
152 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
153 {
154 mCurrentPressure = strokeManager()->getPressure();
155 drawStroke();
156 if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
157 strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
158 }
159 }
160
pointerReleaseEvent(PointerEvent * event)161 void BrushTool::pointerReleaseEvent(PointerEvent *event)
162 {
163 if (event->inputType() != mCurrentInputType) return;
164
165 Layer* layer = mEditor->layers()->currentLayer();
166 mEditor->backup(typeName());
167
168 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
169 if (distance < 1)
170 {
171 paintAt(mMouseDownPoint);
172 }
173 else
174 {
175 drawStroke();
176 }
177
178 if (layer->type() == Layer::BITMAP)
179 paintBitmapStroke();
180 else if (layer->type() == Layer::VECTOR)
181 paintVectorStroke();
182
183 endStroke();
184 }
185
186 // draw a single paint dab at the given location
paintAt(QPointF point)187 void BrushTool::paintAt(QPointF point)
188 {
189 //qDebug() << "Made a single dab at " << point;
190 Layer* layer = mEditor->layers()->currentLayer();
191 if (layer->type() == Layer::BITMAP)
192 {
193 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
194 qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
195 qreal brushWidth = properties.width * pressure;
196 mCurrentWidth = brushWidth;
197
198 BlitRect rect(point.toPoint());
199 mScribbleArea->drawBrush(point,
200 brushWidth,
201 properties.feather,
202 mEditor->color()->frontColor(),
203 opacity,
204 true);
205
206 int rad = qRound(brushWidth) / 2 + 2;
207 mScribbleArea->refreshBitmap(rect, rad);
208 }
209 }
210
drawStroke()211 void BrushTool::drawStroke()
212 {
213 StrokeTool::drawStroke();
214 QList<QPointF> p = strokeManager()->interpolateStroke();
215
216 Layer* layer = mEditor->layers()->currentLayer();
217
218 if (layer->type() == Layer::BITMAP)
219 {
220 for (int i = 0; i < p.size(); i++)
221 {
222 p[i] = mEditor->view()->mapScreenToCanvas(p[i]);
223 }
224
225 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
226 qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
227 qreal brushWidth = properties.width * pressure;
228 mCurrentWidth = brushWidth;
229
230 qreal brushStep = (0.5 * brushWidth);
231 brushStep = qMax(1.0, brushStep);
232
233 BlitRect rect;
234
235 QPointF a = mLastBrushPoint;
236 QPointF b = getCurrentPoint();
237
238 qreal distance = 4 * QLineF(b, a).length();
239 int steps = qRound(distance / brushStep);
240
241 for (int i = 0; i < steps; i++)
242 {
243 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
244
245 rect.extend(point.toPoint());
246 mScribbleArea->drawBrush(point,
247 brushWidth,
248 properties.feather,
249 mEditor->color()->frontColor(),
250 opacity,
251 true);
252 if (i == (steps - 1))
253 {
254 mLastBrushPoint = getCurrentPoint();
255 }
256 }
257
258 int rad = qRound(brushWidth / 2 + 2);
259
260 mScribbleArea->paintBitmapBufferRect(rect);
261 mScribbleArea->refreshBitmap(rect, rad);
262
263 // Line visualizer
264 // for debugging
265 // QPainterPath tempPath;
266
267 // QPointF mappedMousePos = mEditor->view()->mapScreenToCanvas(strokeManager()->getMousePos());
268 // tempPath.moveTo(getCurrentPoint());
269 // tempPath.lineTo(mappedMousePos);
270
271 // QPen pen( Qt::black,
272 // 1,
273 // Qt::SolidLine,
274 // Qt::RoundCap,
275 // Qt::RoundJoin );
276 // mScribbleArea->drawPolyline(tempPath, pen, true);
277
278 }
279 else if (layer->type() == Layer::VECTOR)
280 {
281 qreal pressure = (properties.pressure) ? mCurrentPressure : 1;
282 qreal brushWidth = properties.width * pressure;
283
284 int rad = qRound((brushWidth / 2 + 2) * mEditor->view()->scaling());
285
286 QPen pen(mEditor->color()->frontColor(),
287 brushWidth * mEditor->view()->scaling(),
288 Qt::SolidLine,
289 Qt::RoundCap,
290 Qt::RoundJoin);
291
292 if (p.size() == 4)
293 {
294 QPainterPath path(p[0]);
295 path.cubicTo(p[1], p[2], p[3]);
296
297 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
298 mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
299 }
300 }
301 }
302
paintBitmapStroke()303 void BrushTool::paintBitmapStroke()
304 {
305 mScribbleArea->paintBitmapBuffer();
306 mScribbleArea->clearBitmapBuffer();
307 }
308
309 // This function uses the points from DrawStroke
310 // and turns them into vector lines.
paintVectorStroke()311 void BrushTool::paintVectorStroke()
312 {
313 if (mStrokePoints.empty())
314 return;
315
316 Layer* layer = mEditor->layers()->currentLayer();
317
318 if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1)
319 {
320 // Clear the temporary pixel path
321 mScribbleArea->clearBitmapBuffer();
322 qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
323
324 BezierCurve curve(mStrokePoints, mStrokePressures, tol);
325 curve.setWidth(properties.width);
326 curve.setFeather(properties.feather);
327 curve.setFilled(false);
328 curve.setInvisibility(properties.invisibility);
329 curve.setVariableWidth(properties.pressure);
330 curve.setColorNumber(mEditor->color()->frontColorNumber());
331
332 VectorImage* vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
333 vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
334
335 if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
336 {
337 mEditor->deselectAll();
338 }
339
340 vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
341
342 mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
343 }
344 }
345