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  mMouseDownPoint = getCurrentPoint();
122  mLastBrushPoint = getCurrentPoint();
123 
124  startStroke(event->inputType());
125 }
126 
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 
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
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 
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(),
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 
261 void PenTool::paintBitmapStroke()
262 {
263  mScribbleArea->paintBitmapBuffer();
264  mScribbleArea->clearBitmapBuffer();
265 }
266 
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 }
ShiftModifier
QHash::iterator insert(const Key &key, const T &value)
SolidLine
LeftButton
void setModified(int layerNumber, int frameNumber)
Set frame on layer to modified and invalidate current frame cache.
RoundCap
int size() const const
qreal length() const const
bool empty() const const
bool isAnyCurveSelected()
VectorImage::isAnyCurveSelected.
Definition: layer.h:38
void addCurve(BezierCurve &newCurve, qreal factor, bool interacts=true)
VectorImage::addCurve.
Qt::MouseButtons buttons() const
Returns Qt::MouseButtons()
CompositionMode_Source
RoundJoin
QColor frontColor(bool useIndexedColor=true)
frontColor
QPoint toPoint() const const
int getLastCurveNumber()
VectorImage::getLastCurveNumber.
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.