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 #include "pentool.h"
18 
19 #include <QPixmap>
20 #include <QSettings>
21 
22 #include "vectorimage.h"
23 #include "layervector.h"
24 #include "colormanager.h"
25 #include "strokemanager.h"
26 #include "layermanager.h"
27 #include "viewmanager.h"
28 #include "selectionmanager.h"
29 #include "editor.h"
30 #include "scribblearea.h"
31 #include "blitrect.h"
32 #include "pointerevent.h"
33 
34 
PenTool(QObject * parent)35 PenTool::PenTool(QObject* parent) : StrokeTool(parent)
36 {
37 }
38 
loadSettings()39 void PenTool::loadSettings()
40 {
41     mPropertyEnabled[WIDTH] = true;
42     mPropertyEnabled[PRESSURE] = true;
43     mPropertyEnabled[VECTORMERGE] = true;
44     mPropertyEnabled[ANTI_ALIASING] = true;
45     mPropertyEnabled[STABILIZATION] = true;
46 
47     QSettings settings(PENCIL2D, PENCIL2D);
48 
49     properties.width = settings.value("penWidth", 12.0).toDouble();
50     properties.pressure = settings.value("penPressure", true).toBool();
51     properties.invisibility = OFF;
52     properties.preserveAlpha = OFF;
53     properties.useAA = settings.value("penAA", true).toBool();
54     properties.stabilizerLevel = settings.value("penLineStabilization", StabilizationLevel::STRONG).toInt();
55 
56     mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
57 }
58 
resetToDefault()59 void PenTool::resetToDefault()
60 {
61     setWidth(12.0);
62     setUseFeather(false);
63     setPressure(true);
64     setStabilizerLevel(StabilizationLevel::STRONG);
65     setAA(1);
66 }
67 
setWidth(const qreal width)68 void PenTool::setWidth(const qreal width)
69 {
70     // Set current property
71     properties.width = width;
72 
73     // Update settings
74     QSettings settings(PENCIL2D, PENCIL2D);
75     settings.setValue("penWidth", width);
76     settings.sync();
77 }
78 
setPressure(const bool pressure)79 void PenTool::setPressure(const bool pressure)
80 {
81     // Set current property
82     properties.pressure = pressure;
83 
84     // Update settings
85     QSettings settings(PENCIL2D, PENCIL2D);
86     settings.setValue("penPressure", pressure);
87     settings.sync();
88 }
89 
setAA(const int AA)90 void PenTool::setAA(const int AA)
91 {
92     // Set current property
93     properties.useAA = AA;
94 
95     // Update settings
96     QSettings settings(PENCIL2D, PENCIL2D);
97     settings.setValue("penAA", AA);
98     settings.sync();
99 }
100 
setStabilizerLevel(const int level)101 void PenTool::setStabilizerLevel(const int level)
102 {
103     properties.stabilizerLevel = level;
104 
105     QSettings settings(PENCIL2D, PENCIL2D);
106     settings.setValue("penLineStabilization", level);
107     settings.sync();
108 }
109 
cursor()110 QCursor PenTool::cursor()
111 {
112     if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
113     {
114         return QCursor(QPixmap(":icons/pen.png"), -5, 0);
115     }
116     return QCursor(QPixmap(":icons/cross.png"), 10, 10);
117 }
118 
pointerPressEvent(PointerEvent * event)119 void PenTool::pointerPressEvent(PointerEvent *event)
120 {
121     mMouseDownPoint = getCurrentPoint();
122     mLastBrushPoint = getCurrentPoint();
123 
124     startStroke(event->inputType());
125 }
126 
pointerMoveEvent(PointerEvent * event)127 void PenTool::pointerMoveEvent(PointerEvent* event)
128 {
129     if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
130     {
131         mCurrentPressure = strokeManager()->getPressure();
132         drawStroke();
133         if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
134             strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
135     }
136 }
137 
pointerReleaseEvent(PointerEvent * event)138 void PenTool::pointerReleaseEvent(PointerEvent *event)
139 {
140     if (event->inputType() != mCurrentInputType) return;
141 
142     mEditor->backup(typeName());
143 
144     Layer* layer = mEditor->layers()->currentLayer();
145 
146     qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
147     if (distance < 1)
148     {
149         paintAt(mMouseDownPoint);
150     }
151     else
152     {
153         drawStroke();
154     }
155 
156     if (layer->type() == Layer::BITMAP)
157         paintBitmapStroke();
158     else if (layer->type() == Layer::VECTOR)
159         paintVectorStroke(layer);
160     endStroke();
161 }
162 
163 // draw a single paint dab at the given location
paintAt(QPointF point)164 void PenTool::paintAt(QPointF point)
165 {
166     //qDebug() << "Made a single dab at " << point;
167 
168     Layer* layer = mEditor->layers()->currentLayer();
169     if (layer->type() == Layer::BITMAP)
170     {
171         qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
172         qreal brushWidth = properties.width * pressure;
173         mCurrentWidth = brushWidth;
174 
175         mScribbleArea->drawPen(point,
176                                brushWidth,
177                                mEditor->color()->frontColor(),
178                                properties.useAA);
179 
180         int rad = qRound(brushWidth) / 2 + 2;
181 
182         BlitRect rect(point.toPoint());
183         mScribbleArea->refreshBitmap(rect, rad);
184     }
185 }
186 
drawStroke()187 void PenTool::drawStroke()
188 {
189     StrokeTool::drawStroke();
190     QList<QPointF> p = strokeManager()->interpolateStroke();
191 
192     Layer* layer = mEditor->layers()->currentLayer();
193 
194     if (layer->type() == Layer::BITMAP)
195     {
196         for (int i = 0; i < p.size(); i++)
197         {
198             p[i] = mEditor->view()->mapScreenToCanvas(p[i]);
199         }
200 
201         qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
202         qreal brushWidth = properties.width * pressure;
203         mCurrentWidth = brushWidth;
204 
205         // TODO: Make popup widget for less important properties,
206         // Eg. stepsize should be a slider.. will have fixed (0.3) value for now.
207         qreal brushStep = (0.5 * brushWidth);
208         brushStep = qMax(1.0, brushStep);
209 
210         BlitRect rect;
211 
212         QPointF a = mLastBrushPoint;
213         QPointF b = getCurrentPoint();
214 
215         qreal distance = 4 * QLineF(b, a).length();
216         int steps = qRound(distance / brushStep);
217 
218         for (int i = 0; i < steps; i++)
219         {
220             QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
221             rect.extend(point.toPoint());
222             mScribbleArea->drawPen(point,
223                                    brushWidth,
224                                    mEditor->color()->frontColor(),
225                                    properties.useAA);
226 
227             if (i == (steps - 1))
228             {
229                 mLastBrushPoint = getCurrentPoint();
230             }
231         }
232 
233         int rad = qRound(brushWidth) / 2 + 2;
234 
235         mScribbleArea->paintBitmapBufferRect(rect);
236         mScribbleArea->refreshBitmap(rect, rad);
237     }
238     else if (layer->type() == Layer::VECTOR)
239     {
240         qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
241         qreal brushWidth = properties.width * pressure;
242 
243         int rad = qRound((brushWidth / 2 + 2) * mEditor->view()->scaling());
244 
245         QPen pen(mEditor->color()->frontColor(),
246                  brushWidth * mEditor->view()->scaling(),
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             mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
256             mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
257         }
258     }
259 }
260 
paintBitmapStroke()261 void PenTool::paintBitmapStroke()
262 {
263     mScribbleArea->paintBitmapBuffer();
264     mScribbleArea->clearBitmapBuffer();
265 }
266 
paintVectorStroke(Layer * layer)267 void PenTool::paintVectorStroke(Layer* layer)
268 {
269     if (mStrokePoints.empty())
270         return;
271 
272     // Clear the temporary pixel path
273     mScribbleArea->clearBitmapBuffer();
274     qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
275 
276     BezierCurve curve(mStrokePoints, mStrokePressures, tol);
277     curve.setWidth(properties.width);
278     curve.setFeather(properties.feather);
279     curve.setFilled(false);
280     curve.setInvisibility(properties.invisibility);
281     curve.setVariableWidth(properties.pressure);
282     curve.setColorNumber(mEditor->color()->frontColorNumber());
283 
284     auto pLayerVector = static_cast<LayerVector*>(layer);
285     VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
286     if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
287     vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
288 
289     if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
290     {
291         mEditor->deselectAll();
292     }
293 
294     vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
295 
296     mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
297 }
298