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  startStroke(event->inputType());
153  mLastBrushPoint = getCurrentPoint();
154  mMouseDownPoint = getCurrentPoint();
155 }
156 
157 void EraserTool::pointerMoveEvent(PointerEvent* event)
158 {
159  if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
160  {
161  mCurrentPressure = strokeManager()->getPressure();
162  updateStrokes();
163  if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
164  strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
165  }
166 }
167 
168 void EraserTool::pointerReleaseEvent(PointerEvent *event)
169 {
170  if (event->inputType() != mCurrentInputType) return;
171 
172  mEditor->backup(typeName());
173 
174  qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
175  if (distance < 1)
176  {
177  paintAt(mMouseDownPoint);
178  }
179  else
180  {
181  drawStroke();
182  }
183  removeVectorPaint();
184  endStroke();
185 }
186 
187 // draw a single paint dab at the given location
188 void EraserTool::paintAt(QPointF point)
189 {
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  QColor(255, 255, 255, 255),
203  opacity,
204  properties.useFeather,
205  properties.useAA == ON);
206 
207  int rad = qRound(brushWidth / 2 + 2);
208 
209  //continuously update buffer to update stroke behind grid.
210  mScribbleArea->paintBitmapBufferRect(rect);
211 
212  mScribbleArea->refreshBitmap(rect, rad);
213  }
214 }
215 
216 void EraserTool::drawStroke()
217 {
218  StrokeTool::drawStroke();
219  QList<QPointF> p = strokeManager()->interpolateStroke();
220 
221  Layer* layer = mEditor->layers()->currentLayer();
222 
223  if (layer->type() == Layer::BITMAP)
224  {
225  for (int i = 0; i < p.size(); i++)
226  {
227  p[i] = mEditor->view()->mapScreenToCanvas(p[i]);
228  }
229 
230  qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
231  qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
232  qreal brushWidth = properties.width * pressure;
233  mCurrentWidth = brushWidth;
234 
235  qreal brushStep = (0.5 * brushWidth);
236  brushStep = qMax(1.0, brushStep);
237 
238  BlitRect rect;
239 
240  QPointF a = mLastBrushPoint;
241  QPointF b = getCurrentPoint();
242 
243  qreal distance = 4 * QLineF(b, a).length();
244  int steps = qRound(distance / brushStep);
245 
246  for (int i = 0; i < steps; i++)
247  {
248  QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
249 
250  rect.extend(point.toPoint());
251  mScribbleArea->drawBrush(point,
252  brushWidth,
253  properties.feather,
254  Qt::white,
255  opacity,
256  properties.useFeather,
257  properties.useAA == ON);
258  if (i == (steps - 1))
259  {
260  mLastBrushPoint = getCurrentPoint();
261  }
262  }
263 
264  int rad = qRound(brushWidth / 2 + 2);
265 
266  mScribbleArea->paintBitmapBufferRect(rect);
267  mScribbleArea->refreshBitmap(rect, rad);
268  }
269  else if (layer->type() == Layer::VECTOR)
270  {
271  mCurrentWidth = properties.width;
272  if (properties.pressure)
273  {
274  mCurrentWidth = (mCurrentWidth + (strokeManager()->getPressure() * mCurrentWidth)) * 0.5;
275  }
276  qreal brushWidth = mCurrentWidth;
277 
279  int rad = qRound(brushWidth) / 2 + 2;
280 
281  if (p.size() == 4)
282  {
283  QPainterPath path(p[0]);
284  path.cubicTo(p[1],
285  p[2],
286  p[3]);
287  qDebug() << path;
288  mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
289  mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
290  }
291  }
292 }
293 
294 void EraserTool::removeVectorPaint()
295 {
296  Layer* layer = mEditor->layers()->currentLayer();
297  if (layer->type() == Layer::BITMAP)
298  {
299  mScribbleArea->paintBitmapBuffer();
300  mScribbleArea->clearBitmapBuffer();
301  }
302  else if (layer->type() == Layer::VECTOR)
303  {
304  mScribbleArea->clearBitmapBuffer();
305  VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
306  if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
307  // Clear the area containing the last point
308  //vectorImage->removeArea(lastPoint);
309  // Clear the temporary pixel path
310  vectorImage->deleteSelectedPoints();
311 
312  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
313  }
314 }
315 
316 void EraserTool::updateStrokes()
317 {
318  Layer* layer = mEditor->layers()->currentLayer();
319  if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
320  {
321  drawStroke();
322  }
323 
324  if (layer->type() == Layer::VECTOR)
325  {
326  qreal radius = properties.width / 2;
327 
328  VectorImage* currKey = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
329  QList<VertexRef> nearbyVertices = currKey->getVerticesCloseTo(getCurrentPoint(), radius);
330  for (auto nearbyVertice : nearbyVertices)
331  {
332  currKey->setSelected(nearbyVertice, true);
333  }
334  }
335 }
ShiftModifier
QHash::iterator insert(const Key &key, const T &value)
SolidLine
QList< VertexRef > getVerticesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getVerticesCloseTo.
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
Definition: layer.h:38
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.