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