All Classes Namespaces Functions Variables Enumerations Properties Pages
penciltool.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 "penciltool.h"
18 
19 #include <QSettings>
20 #include <QPixmap>
21 #include "pointerevent.h"
22 
23 #include "layermanager.h"
24 #include "colormanager.h"
25 #include "strokemanager.h"
26 #include "viewmanager.h"
27 #include "preferencemanager.h"
28 #include "selectionmanager.h"
29 
30 #include "editor.h"
31 #include "scribblearea.h"
32 #include "blitrect.h"
33 #include "layervector.h"
34 #include "vectorimage.h"
35 
36 
37 PencilTool::PencilTool(QObject* parent) : StrokeTool(parent)
38 {
39 }
40 
41 void PencilTool::loadSettings()
42 {
43  mPropertyEnabled[WIDTH] = true;
44  mPropertyEnabled[PRESSURE] = true;
45  mPropertyEnabled[VECTORMERGE] = false;
46  mPropertyEnabled[STABILIZATION] = true;
47  mPropertyEnabled[FILLCONTOUR] = true;
48 
49  QSettings settings(PENCIL2D, PENCIL2D);
50  properties.width = settings.value("pencilWidth", 4).toDouble();
51  properties.feather = 50;
52  properties.pressure = settings.value("pencilPressure", true).toBool();
53  properties.stabilizerLevel = settings.value("pencilLineStabilization", StabilizationLevel::STRONG).toInt();
54  properties.useAA = DISABLED;
55  properties.useFeather = true;
56  properties.useFillContour = false;
57  // properties.invisibility = 1;
58  // properties.preserveAlpha = 0;
59 
60  mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
61 }
62 
63 void PencilTool::resetToDefault()
64 {
65  setWidth(4.0);
66  setFeather(50);
67  setUseFeather(true);
68  setStabilizerLevel(StabilizationLevel::STRONG);
69 }
70 
71 void PencilTool::setWidth(const qreal width)
72 {
73  // Set current property
74  properties.width = width;
75 
76  // Update settings
77  QSettings settings(PENCIL2D, PENCIL2D);
78  settings.setValue("pencilWidth", width);
79  settings.sync();
80 }
81 
82 void PencilTool::setFeather(const qreal feather)
83 {
84  properties.feather = feather;
85 }
86 
87 void PencilTool::setUseFeather(const bool usingFeather)
88 {
89  // Set current property
90  properties.useFeather = usingFeather;
91 
92  // Update settings
93  QSettings settings(PENCIL2D, PENCIL2D);
94  settings.setValue("brushUseFeather", usingFeather);
95  settings.sync();
96 }
97 
98 void PencilTool::setInvisibility(const bool)
99 {
100  // force value
101  properties.invisibility = 1;
102 }
103 
104 void PencilTool::setPressure(const bool pressure)
105 {
106  // Set current property
107  properties.pressure = pressure;
108 
109  // Update settings
110  QSettings settings(PENCIL2D, PENCIL2D);
111  settings.setValue("pencilPressure", pressure);
112  settings.sync();
113 }
114 
115 void PencilTool::setPreserveAlpha(const bool preserveAlpha)
116 {
117  // force value
118  Q_UNUSED( preserveAlpha );
119  properties.preserveAlpha = 0;
120 }
121 
122 void PencilTool::setStabilizerLevel(const int level)
123 {
124  properties.stabilizerLevel = level;
125 
126  QSettings settings(PENCIL2D, PENCIL2D);
127  settings.setValue("pencilLineStabilization", level);
128  settings.sync();
129 }
130 
131 void PencilTool::setUseFillContour(const bool useFillContour)
132 {
133  properties.useFillContour = useFillContour;
134 
135  QSettings settings(PENCIL2D, PENCIL2D);
136  settings.setValue("FillContour", useFillContour);
137  settings.sync();
138 }
139 
140 QCursor PencilTool::cursor()
141 {
142  if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
143  {
144  return QCursor(QPixmap(":icons/pencil2.png"), 0, 16);
145  }
146  return QCursor(QPixmap(":icons/cross.png"), 10, 10);
147 }
148 
149 void PencilTool::pointerPressEvent(PointerEvent *event)
150 {
151  mScribbleArea->setAllDirty();
152 
153  mMouseDownPoint = getCurrentPoint();
154  mLastBrushPoint = getCurrentPoint();
155 
156  startStroke(event->inputType());
157 
158  // note: why are we doing this on device press event?
159  if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR && !mEditor->preference()->isOn(SETTING::INVISIBLE_LINES))
160  {
161  mScribbleArea->toggleThinLines();
162  }
163 }
164 
165 void PencilTool::pointerMoveEvent(PointerEvent* event)
166 {
167  if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
168  {
169  mCurrentPressure = strokeManager()->getPressure();
170  drawStroke();
171  if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
172  strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
173  }
174 }
175 
176 void PencilTool::pointerReleaseEvent(PointerEvent *event)
177 {
178  if (event->inputType() != mCurrentInputType) return;
179 
180  mEditor->backup(typeName());
181  qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
182  if (distance < 1)
183  {
184  paintAt(mMouseDownPoint);
185  }
186  else
187  {
188  drawStroke();
189  }
190 
191  Layer* layer = mEditor->layers()->currentLayer();
192  if (layer->type() == Layer::BITMAP)
193  paintBitmapStroke();
194  else if (layer->type() == Layer::VECTOR)
195  paintVectorStroke(layer);
196  endStroke();
197 }
198 
199 // draw a single paint dab at the given location
200 void PencilTool::paintAt(QPointF point)
201 {
202  //qDebug() << "Made a single dab at " << point;
203  Layer* layer = mEditor->layers()->currentLayer();
204  if (layer->type() == Layer::BITMAP)
205  {
206  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
207  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
208  qreal brushWidth = properties.width * pressure;
209  qreal fixedBrushFeather = properties.feather;
210 
211  mCurrentWidth = brushWidth;
212 
213  BlitRect rect(point.toPoint());
214  mScribbleArea->drawPencil(point,
215  brushWidth,
216  fixedBrushFeather,
217  mEditor->color()->frontColor(),
218  opacity);
219 
220  int rad = qRound(brushWidth) / 2 + 2;
221  mScribbleArea->refreshBitmap(rect, rad);
222  }
223 }
224 
225 
226 void PencilTool::drawStroke()
227 {
228  StrokeTool::drawStroke();
229  QList<QPointF> p = strokeManager()->interpolateStroke();
230 
231  Layer* layer = mEditor->layers()->currentLayer();
232 
233  if (layer->type() == Layer::BITMAP)
234  {
235  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
236  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
237  qreal brushWidth = properties.width * pressure;
238  mCurrentWidth = brushWidth;
239 
240  qreal fixedBrushFeather = properties.feather;
241  qreal brushStep = qMax(1.0, (0.5 * brushWidth));
242 
243  BlitRect rect;
244 
245  QPointF a = mLastBrushPoint;
246  QPointF b = getCurrentPoint();
247 
248  qreal distance = 4 * QLineF(b, a).length();
249  int steps = qRound(distance / brushStep);
250 
251  for (int i = 0; i < steps; i++)
252  {
253  QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
254  rect.extend(point.toPoint());
255  mScribbleArea->drawPencil(point,
256  brushWidth,
257  fixedBrushFeather,
258  mEditor->color()->frontColor(),
259  opacity);
260 
261  if (i == (steps - 1))
262  {
263  mLastBrushPoint = getCurrentPoint();
264  }
265  }
266 
267  int rad = qRound(brushWidth) / 2 + 2;
268 
269  mScribbleArea->paintBitmapBufferRect(rect);
270  mScribbleArea->refreshBitmap(rect, rad);
271  }
272  else if (layer->type() == Layer::VECTOR)
273  {
274  properties.useFeather = false;
275  mCurrentWidth = 0; // FIXME: WTF?
276  QPen pen(mEditor->color()->frontColor(),
277  1,
278  Qt::DotLine,
279  Qt::RoundCap,
280  Qt::RoundJoin);
281 
282  int rad = qRound((mCurrentWidth / 2 + 2) * mEditor->view()->scaling());
283 
284  if (p.size() == 4)
285  {
286  QPainterPath path(p[0]);
287  path.cubicTo(p[1],
288  p[2],
289  p[3]);
290  mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
291  mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
292  }
293  }
294 }
295 
296 
297 void PencilTool::paintBitmapStroke()
298 {
299  mScribbleArea->paintBitmapBuffer();
300  mScribbleArea->setAllDirty();
301  mScribbleArea->clearBitmapBuffer();
302 }
303 
304 void PencilTool::paintVectorStroke(Layer* layer)
305 {
306  if (mStrokePoints.empty())
307  return;
308 
309  // Clear the temporary pixel path
310  mScribbleArea->clearBitmapBuffer();
311  qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
312 
313  BezierCurve curve(mStrokePoints, mStrokePressures, tol);
314  curve.setWidth(0);
315  curve.setFeather(0);
316  curve.setFilled(false);
317  curve.setInvisibility(true);
318  curve.setVariableWidth(false);
319  curve.setColorNumber(mEditor->color()->frontColorNumber());
320  VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
321  if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
322  vectorImage->addCurve(curve, qAbs(mEditor->view()->scaling()), properties.vectorMergeEnabled);
323 
324  if (properties.useFillContour)
325  {
326  vectorImage->fillContour(mStrokePoints,
327  mEditor->color()->frontColorNumber());
328  }
329 
330  if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
331  {
332  mEditor->deselectAll();
333  }
334 
335  // select last/newest curve
336  vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
337 
338  // TODO: selection doesn't apply on enter
339 
340  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
341  mScribbleArea->setAllDirty();
342 }
ShiftModifier
QHash::iterator insert(const Key &key, const T &value)
LeftButton
void fillContour(QList< QPointF > contourPath, int color)
VectorImage::fillContour.
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.