All Classes Namespaces Functions Variables Enumerations Properties Pages
erasertool.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 "erasertool.h"
18 
19 #include <QSettings>
20 #include <QPixmap>
21 #include <QPainter>
22 
23 #include "editor.h"
24 #include "blitrect.h"
25 #include "scribblearea.h"
26 #include "strokemanager.h"
27 #include "layermanager.h"
28 #include "viewmanager.h"
29 #include "layervector.h"
30 #include "vectorimage.h"
31 #include "pointerevent.h"
32 
33 
34 EraserTool::EraserTool(QObject* parent) : StrokeTool(parent)
35 {
36 }
37 
38 ToolType EraserTool::type()
39 {
40  return ERASER;
41 }
42 
43 void EraserTool::loadSettings()
44 {
45  mPropertyEnabled[WIDTH] = true;
46  mPropertyEnabled[USEFEATHER] = true;
47  mPropertyEnabled[FEATHER] = true;
48  mPropertyEnabled[USEFEATHER] = true;
49  mPropertyEnabled[PRESSURE] = true;
50  mPropertyEnabled[STABILIZATION] = true;
51  mPropertyEnabled[ANTI_ALIASING] = true;
52 
53  QSettings settings(PENCIL2D, PENCIL2D);
54 
55  properties.width = settings.value("eraserWidth", 24.0).toDouble();
56  properties.feather = settings.value("eraserFeather", 48.0).toDouble();
57  properties.useFeather = settings.value("eraserUseFeather", true).toBool();
58  properties.pressure = settings.value("eraserPressure", true).toBool();
59  properties.invisibility = DISABLED;
60  properties.preserveAlpha = OFF;
61  properties.stabilizerLevel = settings.value("stabilizerLevel", StabilizationLevel::NONE).toInt();
62  properties.useAA = settings.value("eraserAA", 1).toInt();
63 
64  if (properties.useFeather) { properties.useAA = -1; }
65 
66  mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
67  mQuickSizingProperties.insert(Qt::ControlModifier, FEATHER);
68 }
69 
70 void EraserTool::resetToDefault()
71 {
72  setWidth(24.0);
73  setFeather(48.0);
74  setUseFeather(true);
75  setPressure(true);
76  setAA(true);
77  setStabilizerLevel(StabilizationLevel::NONE);
78 }
79 
80 void EraserTool::setWidth(const qreal width)
81 {
82  // Set current property
83  properties.width = width;
84 
85  // Update settings
86  QSettings settings(PENCIL2D, PENCIL2D);
87  settings.setValue("eraserWidth", width);
88  settings.sync();
89 }
90 
91 void EraserTool::setUseFeather(const bool usingFeather)
92 {
93  // Set current property
94  properties.useFeather = usingFeather;
95 
96  // Update settings
97  QSettings settings(PENCIL2D, PENCIL2D);
98  settings.setValue("eraserUseFeather", usingFeather);
99  settings.sync();
100 }
101 
102 void EraserTool::setFeather(const qreal feather)
103 {
104  // Set current property
105  properties.feather = feather;
106 
107  // Update settings
108  QSettings settings(PENCIL2D, PENCIL2D);
109  settings.setValue("eraserFeather", feather);
110  settings.sync();
111 }
112 
113 void EraserTool::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("eraserPressure", pressure);
121  settings.sync();
122 }
123 
124 void EraserTool::setAA(const int AA)
125 {
126  // Set current property
127  properties.useAA = AA;
128 
129  // Update settings
130  QSettings settings(PENCIL2D, PENCIL2D);
131  settings.setValue("eraserAA", AA);
132  settings.sync();
133 }
134 
135 void EraserTool::setStabilizerLevel(const int level)
136 {
137  properties.stabilizerLevel = level;
138 
139  QSettings settings(PENCIL2D, PENCIL2D);
140  settings.setValue("stabilizerLevel", level);
141  settings.sync();
142 }
143 
144 
145 QCursor EraserTool::cursor()
146 {
147  return QCursor(QPixmap(":icons/cross.png"), 10, 10);
148 }
149 
150 void EraserTool::pointerPressEvent(PointerEvent *event)
151 {
152  mScribbleArea->setAllDirty();
153 
154  startStroke(event->inputType());
155  mLastBrushPoint = getCurrentPoint();
156  mMouseDownPoint = getCurrentPoint();
157 }
158 
159 void EraserTool::pointerMoveEvent(PointerEvent* event)
160 {
161  if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
162  {
163  mCurrentPressure = strokeManager()->getPressure();
164  updateStrokes();
165  if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
166  strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
167  }
168 }
169 
170 void EraserTool::pointerReleaseEvent(PointerEvent *event)
171 {
172  if (event->inputType() != mCurrentInputType) return;
173 
174  mEditor->backup(typeName());
175 
176  qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
177  if (distance < 1)
178  {
179  paintAt(mMouseDownPoint);
180  }
181  else
182  {
183  drawStroke();
184  }
185  removeVectorPaint();
186  endStroke();
187 }
188 
189 // draw a single paint dab at the given location
190 void EraserTool::paintAt(QPointF point)
191 {
192  Layer* layer = mEditor->layers()->currentLayer();
193  if (layer->type() == Layer::BITMAP)
194  {
195  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
196  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
197  qreal brushWidth = properties.width * pressure;
198  mCurrentWidth = brushWidth;
199 
200  BlitRect rect(point.toPoint());
201  mScribbleArea->drawBrush(point,
202  brushWidth,
203  properties.feather,
204  QColor(255, 255, 255, 255),
205  opacity,
206  properties.useFeather,
207  properties.useAA == ON);
208 
209  int rad = qRound(brushWidth / 2 + 2);
210 
211  //continuously update buffer to update stroke behind grid.
212  mScribbleArea->paintBitmapBufferRect(rect);
213 
214  mScribbleArea->refreshBitmap(rect, rad);
215  }
216 }
217 
218 void EraserTool::drawStroke()
219 {
220  StrokeTool::drawStroke();
221  QList<QPointF> p = strokeManager()->interpolateStroke();
222 
223  Layer* layer = mEditor->layers()->currentLayer();
224 
225  if (layer->type() == Layer::BITMAP)
226  {
227  for (int i = 0; i < p.size(); i++)
228  {
229  p[i] = mEditor->view()->mapScreenToCanvas(p[i]);
230  }
231 
232  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
233  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
234  qreal brushWidth = properties.width * pressure;
235  mCurrentWidth = brushWidth;
236 
237  qreal brushStep = (0.5 * brushWidth);
238  brushStep = qMax(1.0, brushStep);
239 
240  BlitRect rect;
241 
242  QPointF a = mLastBrushPoint;
243  QPointF b = getCurrentPoint();
244 
245  qreal distance = 4 * QLineF(b, a).length();
246  int steps = qRound(distance / brushStep);
247 
248  for (int i = 0; i < steps; i++)
249  {
250  QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
251 
252  rect.extend(point.toPoint());
253  mScribbleArea->drawBrush(point,
254  brushWidth,
255  properties.feather,
256  Qt::white,
257  opacity,
258  properties.useFeather,
259  properties.useAA == ON);
260  if (i == (steps - 1))
261  {
262  mLastBrushPoint = getCurrentPoint();
263  }
264  }
265 
266  int rad = qRound(brushWidth / 2 + 2);
267 
268  mScribbleArea->paintBitmapBufferRect(rect);
269  mScribbleArea->refreshBitmap(rect, rad);
270  }
271  else if (layer->type() == Layer::VECTOR)
272  {
273  mCurrentWidth = properties.width;
274  if (properties.pressure)
275  {
276  mCurrentWidth = (mCurrentWidth + (strokeManager()->getPressure() * mCurrentWidth)) * 0.5;
277  }
278  qreal brushWidth = mCurrentWidth;
279 
281  int rad = qRound(brushWidth) / 2 + 2;
282 
283  if (p.size() == 4)
284  {
285  QPainterPath path(p[0]);
286  path.cubicTo(p[1],
287  p[2],
288  p[3]);
289  qDebug() << path;
290  mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
291  mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
292  }
293  }
294 }
295 
296 void EraserTool::removeVectorPaint()
297 {
298  Layer* layer = mEditor->layers()->currentLayer();
299  if (layer->type() == Layer::BITMAP)
300  {
301  mScribbleArea->paintBitmapBuffer();
302  mScribbleArea->setAllDirty();
303  mScribbleArea->clearBitmapBuffer();
304  }
305  else if (layer->type() == Layer::VECTOR)
306  {
307  mScribbleArea->clearBitmapBuffer();
308  VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
309  if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
310  // Clear the area containing the last point
311  //vectorImage->removeArea(lastPoint);
312  // Clear the temporary pixel path
313  vectorImage->deleteSelectedPoints();
314 
315  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
316  mScribbleArea->setAllDirty();
317  }
318 }
319 
320 void EraserTool::updateStrokes()
321 {
322  Layer* layer = mEditor->layers()->currentLayer();
323  if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
324  {
325  drawStroke();
326  }
327 
328  if (layer->type() == Layer::VECTOR)
329  {
330  qreal radius = properties.width / 2;
331 
332  VectorImage* currKey = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
333  QList<VertexRef> nearbyVertices = currKey->getVerticesCloseTo(getCurrentPoint(), radius);
334  for (auto nearbyVertice : nearbyVertices)
335  {
336  currKey->setSelected(nearbyVertice, true);
337  }
338  mScribbleArea->setAllDirty();
339  }
340 }
ShiftModifier
QHash::iterator insert(const Key &key, const T &value)
SolidLine
QList< VertexRef > getVerticesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getVerticesCloseTo.
LeftButton
RoundCap
int size() const const
qreal length() const const
Definition: layer.h:39
void deleteSelectedPoints()
VectorImage::deleteSelectedPoints.
Qt::MouseButtons buttons() const
Returns Qt::MouseButtons()
CompositionMode_Source
RoundJoin
QPoint toPoint() const const
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.