All Classes Namespaces Functions Variables Enumerations Properties Pages
pentool.cpp
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 
35 PenTool::PenTool(QObject* parent) : StrokeTool(parent)
36 {
37 }
38 
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 
59 void PenTool::resetToDefault()
60 {
61  setWidth(12.0);
62  setUseFeather(false);
63  setPressure(true);
64  setStabilizerLevel(StabilizationLevel::STRONG);
65  setAA(1);
66 }
67 
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 
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 
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 
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 
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 
119 void PenTool::pointerPressEvent(PointerEvent *event)
120 {
121  mScribbleArea->setAllDirty();
122 
123  mMouseDownPoint = getCurrentPoint();
124  mLastBrushPoint = getCurrentPoint();
125 
126  startStroke(event->inputType());
127 }
128 
129 void PenTool::pointerMoveEvent(PointerEvent* event)
130 {
131  if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
132  {
133  mCurrentPressure = strokeManager()->getPressure();
134  drawStroke();
135  if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
136  strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
137  }
138 }
139 
140 void PenTool::pointerReleaseEvent(PointerEvent *event)
141 {
142  if (event->inputType() != mCurrentInputType) return;
143 
144  mEditor->backup(typeName());
145 
146  Layer* layer = mEditor->layers()->currentLayer();
147 
148  qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
149  if (distance < 1)
150  {
151  paintAt(mMouseDownPoint);
152  }
153  else
154  {
155  drawStroke();
156  }
157 
158  if (layer->type() == Layer::BITMAP)
159  paintBitmapStroke();
160  else if (layer->type() == Layer::VECTOR)
161  paintVectorStroke(layer);
162  endStroke();
163 }
164 
165 // draw a single paint dab at the given location
166 void PenTool::paintAt(QPointF point)
167 {
168  //qDebug() << "Made a single dab at " << point;
169 
170  Layer* layer = mEditor->layers()->currentLayer();
171  if (layer->type() == Layer::BITMAP)
172  {
173  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
174  qreal brushWidth = properties.width * pressure;
175  mCurrentWidth = brushWidth;
176 
177  mScribbleArea->drawPen(point,
178  brushWidth,
179  mEditor->color()->frontColor(),
180  properties.useAA);
181 
182  int rad = qRound(brushWidth) / 2 + 2;
183 
184  BlitRect rect(point.toPoint());
185  mScribbleArea->refreshBitmap(rect, rad);
186  }
187 }
188 
189 void PenTool::drawStroke()
190 {
191  StrokeTool::drawStroke();
192  QList<QPointF> p = strokeManager()->interpolateStroke();
193 
194  Layer* layer = mEditor->layers()->currentLayer();
195 
196  if (layer->type() == Layer::BITMAP)
197  {
198  for (int i = 0; i < p.size(); i++)
199  {
200  p[i] = mEditor->view()->mapScreenToCanvas(p[i]);
201  }
202 
203  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
204  qreal brushWidth = properties.width * pressure;
205  mCurrentWidth = brushWidth;
206 
207  // TODO: Make popup widget for less important properties,
208  // Eg. stepsize should be a slider.. will have fixed (0.3) value for now.
209  qreal brushStep = (0.5 * brushWidth);
210  brushStep = qMax(1.0, brushStep);
211 
212  BlitRect rect;
213 
214  QPointF a = mLastBrushPoint;
215  QPointF b = getCurrentPoint();
216 
217  qreal distance = 4 * QLineF(b, a).length();
218  int steps = qRound(distance / brushStep);
219 
220  for (int i = 0; i < steps; i++)
221  {
222  QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
223  rect.extend(point.toPoint());
224  mScribbleArea->drawPen(point,
225  brushWidth,
226  mEditor->color()->frontColor(),
227  properties.useAA);
228 
229  if (i == (steps - 1))
230  {
231  mLastBrushPoint = getCurrentPoint();
232  }
233  }
234 
235  int rad = qRound(brushWidth) / 2 + 2;
236 
237  mScribbleArea->paintBitmapBufferRect(rect);
238  mScribbleArea->refreshBitmap(rect, rad);
239  }
240  else if (layer->type() == Layer::VECTOR)
241  {
242  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
243  qreal brushWidth = properties.width * pressure;
244 
245  int rad = qRound((brushWidth / 2 + 2) * mEditor->view()->scaling());
246 
247  QPen pen(mEditor->color()->frontColor(),
248  brushWidth * mEditor->view()->scaling(),
250  Qt::RoundCap,
251  Qt::RoundJoin);
252 
253  if (p.size() == 4)
254  {
255  QPainterPath path(p[0]);
256  path.cubicTo(p[1], p[2], p[3]);
257  mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
258  mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
259  }
260  }
261 }
262 
263 void PenTool::paintBitmapStroke()
264 {
265  mScribbleArea->paintBitmapBuffer();
266  mScribbleArea->setAllDirty();
267  mScribbleArea->clearBitmapBuffer();
268 }
269 
270 void PenTool::paintVectorStroke(Layer* layer)
271 {
272  if (mStrokePoints.empty())
273  return;
274 
275  // Clear the temporary pixel path
276  mScribbleArea->clearBitmapBuffer();
277  qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
278 
279  BezierCurve curve(mStrokePoints, mStrokePressures, tol);
280  curve.setWidth(properties.width);
281  curve.setFeather(properties.feather);
282  curve.setFilled(false);
283  curve.setInvisibility(properties.invisibility);
284  curve.setVariableWidth(properties.pressure);
285  curve.setColorNumber(mEditor->color()->frontColorNumber());
286 
287  auto pLayerVector = static_cast<LayerVector*>(layer);
288  VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
289  if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
290  vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
291 
292  if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
293  {
294  mEditor->deselectAll();
295  }
296 
297  vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
298 
299  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
300  mScribbleArea->setAllDirty();
301 }
ShiftModifier
QHash::iterator insert(const Key &key, const T &value)
SolidLine
LeftButton
RoundCap
int size() const const
qreal length() const const
bool empty() const const
bool isAnyCurveSelected()
VectorImage::isAnyCurveSelected.
Definition: layer.h:39
void addCurve(BezierCurve &newCurve, qreal factor, bool interacts=true)
VectorImage::addCurve.
Qt::MouseButtons buttons() const
Returns Qt::MouseButtons()
CompositionMode_Source
RoundJoin
QPoint toPoint() const const
int getLastCurveNumber()
VectorImage::getLastCurveNumber.
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.