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  mMouseDownPoint = getCurrentPoint();
145  mLastBrushPoint = getCurrentPoint();
146 
147  startStroke(event->inputType());
148 }
149 
150 void BrushTool::pointerMoveEvent(PointerEvent* event)
151 {
152  if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
153  {
154  mCurrentPressure = strokeManager()->getPressure();
155  drawStroke();
156  if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
157  strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
158  }
159 }
160 
161 void BrushTool::pointerReleaseEvent(PointerEvent *event)
162 {
163  if (event->inputType() != mCurrentInputType) return;
164 
165  Layer* layer = mEditor->layers()->currentLayer();
166  mEditor->backup(typeName());
167 
168  qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
169  if (distance < 1)
170  {
171  paintAt(mMouseDownPoint);
172  }
173  else
174  {
175  drawStroke();
176  }
177 
178  if (layer->type() == Layer::BITMAP)
179  paintBitmapStroke();
180  else if (layer->type() == Layer::VECTOR)
181  paintVectorStroke();
182 
183  endStroke();
184 }
185 
186 // draw a single paint dab at the given location
187 void BrushTool::paintAt(QPointF point)
188 {
189  //qDebug() << "Made a single dab at " << point;
190  Layer* layer = mEditor->layers()->currentLayer();
191  if (layer->type() == Layer::BITMAP)
192  {
193  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
194  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
195  qreal brushWidth = properties.width * pressure;
196  mCurrentWidth = brushWidth;
197 
198  BlitRect rect(point.toPoint());
199  mScribbleArea->drawBrush(point,
200  brushWidth,
201  properties.feather,
202  mEditor->color()->frontColor(),
203  opacity,
204  true);
205 
206  int rad = qRound(brushWidth) / 2 + 2;
207  mScribbleArea->refreshBitmap(rect, rad);
208  }
209 }
210 
211 void BrushTool::drawStroke()
212 {
213  StrokeTool::drawStroke();
214  QList<QPointF> p = strokeManager()->interpolateStroke();
215 
216  Layer* layer = mEditor->layers()->currentLayer();
217 
218  if (layer->type() == Layer::BITMAP)
219  {
220  for (int i = 0; i < p.size(); i++)
221  {
222  p[i] = mEditor->view()->mapScreenToCanvas(p[i]);
223  }
224 
225  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
226  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
227  qreal brushWidth = properties.width * pressure;
228  mCurrentWidth = brushWidth;
229 
230  qreal brushStep = (0.5 * brushWidth);
231  brushStep = qMax(1.0, brushStep);
232 
233  BlitRect rect;
234 
235  QPointF a = mLastBrushPoint;
236  QPointF b = getCurrentPoint();
237 
238  qreal distance = 4 * QLineF(b, a).length();
239  int steps = qRound(distance / brushStep);
240 
241  for (int i = 0; i < steps; i++)
242  {
243  QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
244 
245  rect.extend(point.toPoint());
246  mScribbleArea->drawBrush(point,
247  brushWidth,
248  properties.feather,
249  mEditor->color()->frontColor(),
250  opacity,
251  true);
252  if (i == (steps - 1))
253  {
254  mLastBrushPoint = getCurrentPoint();
255  }
256  }
257 
258  int rad = qRound(brushWidth / 2 + 2);
259 
260  mScribbleArea->paintBitmapBufferRect(rect);
261  mScribbleArea->refreshBitmap(rect, rad);
262 
263  // Line visualizer
264  // for debugging
265 // QPainterPath tempPath;
266 
267 // QPointF mappedMousePos = mEditor->view()->mapScreenToCanvas(strokeManager()->getMousePos());
268 // tempPath.moveTo(getCurrentPoint());
269 // tempPath.lineTo(mappedMousePos);
270 
271 // QPen pen( Qt::black,
272 // 1,
273 // Qt::SolidLine,
274 // Qt::RoundCap,
275 // Qt::RoundJoin );
276 // mScribbleArea->drawPolyline(tempPath, pen, true);
277 
278  }
279  else if (layer->type() == Layer::VECTOR)
280  {
281  qreal pressure = (properties.pressure) ? mCurrentPressure : 1;
282  qreal brushWidth = properties.width * pressure;
283 
284  int rad = qRound((brushWidth / 2 + 2) * mEditor->view()->scaling());
285 
286  QPen pen(mEditor->color()->frontColor(),
287  brushWidth * mEditor->view()->scaling(),
289  Qt::RoundCap,
290  Qt::RoundJoin);
291 
292  if (p.size() == 4)
293  {
294  QPainterPath path(p[0]);
295  path.cubicTo(p[1], p[2], p[3]);
296 
297  mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
298  mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
299  }
300  }
301 }
302 
303 void BrushTool::paintBitmapStroke()
304 {
305  mScribbleArea->paintBitmapBuffer();
306  mScribbleArea->clearBitmapBuffer();
307 }
308 
309 // This function uses the points from DrawStroke
310 // and turns them into vector lines.
311 void BrushTool::paintVectorStroke()
312 {
313  if (mStrokePoints.empty())
314  return;
315 
316  Layer* layer = mEditor->layers()->currentLayer();
317 
318  if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1)
319  {
320  // Clear the temporary pixel path
321  mScribbleArea->clearBitmapBuffer();
322  qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
323 
324  BezierCurve curve(mStrokePoints, mStrokePressures, tol);
325  curve.setWidth(properties.width);
326  curve.setFeather(properties.feather);
327  curve.setFilled(false);
328  curve.setInvisibility(properties.invisibility);
329  curve.setVariableWidth(properties.pressure);
330  curve.setColorNumber(mEditor->color()->frontColorNumber());
331 
332  VectorImage* vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
333  vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
334 
335  if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
336  {
337  mEditor->deselectAll();
338  }
339 
340  vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
341 
342  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
343  }
344 }
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.