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