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