Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
smudgetool.cpp
1 /*
2 
3 Pencil - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2017 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 <QPixmap>
19 #include "editor.h"
20 #include "scribblearea.h"
21 
22 #include "layermanager.h"
23 #include "colormanager.h"
24 #include "layerbitmap.h"
25 #include "layervector.h"
26 #include "strokemanager.h"
27 #include "blitrect.h"
28 
29 #include "smudgetool.h"
30 
31 SmudgeTool::SmudgeTool(QObject *parent) :
32  StrokeTool(parent)
33 {
34  toolMode = 0; // tool mode
35 }
36 
37 ToolType SmudgeTool::type()
38 {
39  return SMUDGE;
40 }
41 
42 void SmudgeTool::loadSettings()
43 {
44  m_enabledProperties[WIDTH] = true;
45  m_enabledProperties[FEATHER] = true;
46 
47 
48  QSettings settings( PENCIL2D, PENCIL2D );
49  properties.width = settings.value("smudgeWidth").toDouble();
50  properties.feather = settings.value("smudgeFeather").toDouble();
51  properties.pressure = 0;
52  properties.inpolLevel = -1;
53 
54  // First run
55  if (properties.width <= 0)
56  {
57  setWidth(25);
58  setFeather(200);
59  setPressure(0);
60  }
61 }
62 
63 void SmudgeTool::setWidth(const qreal width)
64 {
65  // Set current property
66  properties.width = width;
67 
68  // Update settings
69  QSettings settings( PENCIL2D, PENCIL2D );
70  settings.setValue("smudgeWidth", width);
71  settings.sync();
72 }
73 
74 void SmudgeTool::setFeather( const qreal feather )
75 {
76  // Set current property
77  properties.feather = feather;
78 
79  // Update settings
80  QSettings settings( PENCIL2D, PENCIL2D );
81  settings.setValue("smudgeFeather", feather);
82  settings.sync();
83 }
84 
85 void SmudgeTool::setPressure( const bool pressure )
86 {
87  // Set current property
88  properties.pressure = pressure;
89 
90  // Update settings
91  QSettings settings( PENCIL2D, PENCIL2D );
92  settings.setValue("smudgePressure", pressure);
93  settings.sync();
94 }
95 
96 QCursor SmudgeTool::cursor()
97 {
98  qDebug() << "smudge tool";
99  if ( toolMode == 0 ) { //normal mode
100  return QCursor(QPixmap(":icons/smudge.png"),0 ,16);
101  } else { // blured mode
102  return QCursor(QPixmap(":icons/liquify.png"),-4,16);
103  }
104 }
105 
106 void SmudgeTool::adjustPressureSensitiveProperties(qreal pressure, bool mouseDevice)
107 {
108  mCurrentWidth = properties.width;
109  if (properties.pressure && !mouseDevice)
110  {
111  mCurrentPressure = pressure;
112  }
113  else
114  {
115  mCurrentPressure = 1.0;
116  }
117 }
118 
119 bool SmudgeTool::keyPressEvent(QKeyEvent *event)
120 {
121  if (event->key() == Qt::Key_Alt)
122  {
123  toolMode = 1; // alternative mode
124  mScribbleArea->setCursor( cursor() ); // update cursor
125  return true;
126  }
127  return false;
128 }
129 
130 bool SmudgeTool::keyReleaseEvent(QKeyEvent*)
131 {
132 
133  toolMode = 0; // default mode
134  mScribbleArea->setCursor( cursor() ); // update cursor
135 
136  return true;
137 }
138 
139 void SmudgeTool::mousePressEvent(QMouseEvent *event)
140 {
141  //qDebug() << "smudgetool: mousePressEvent";
142 
143  Layer* layer = mEditor->layers()->currentLayer();
144  if (layer == NULL) { return; }
145 
146  if (event->button() == Qt::LeftButton)
147  {
148  if (layer->type() == Layer::BITMAP)
149  {
150  mEditor->backup(typeName());
151  mScribbleArea->setAllDirty();
152  startStroke();
153  mLastBrushPoint = getCurrentPoint();
154  }
155  else if (layer->type() == Layer::VECTOR)
156  {
157  mScribbleArea->mClosestCurves = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0)
158  ->getCurvesCloseTo( getCurrentPoint(), mScribbleArea->tol / mEditor->view()->scaling() );
159  mScribbleArea->mClosestVertices = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0)
160  ->getVerticesCloseTo( getCurrentPoint(), mScribbleArea->tol / mEditor->view()->scaling() );
161 
162  if (mScribbleArea->mClosestVertices.size() > 0 || mScribbleArea->mClosestCurves.size() > 0) // the user clicks near a vertex or a curve
163  {
164  //qDebug() << "closestCurves:" << closestCurves << " | closestVertices" << closestVertices;
165  mEditor->backup(typeName());
166  VectorImage *vectorImage = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
167 
168  if (event->modifiers() != Qt::ShiftModifier && !vectorImage->isSelected(mScribbleArea->mClosestVertices))
169  {
170  mScribbleArea->paintTransformedSelection();
171  mScribbleArea->deselectAll();
172  }
173 
174  vectorImage->setSelected(mScribbleArea->mClosestVertices, true);
175  mScribbleArea->vectorSelection.add(mScribbleArea->mClosestCurves);
176  mScribbleArea->vectorSelection.add(mScribbleArea->mClosestVertices);
177 
178  mScribbleArea->update();
179  }
180  else
181  {
182  mScribbleArea->deselectAll();
183  }
184  }
185  }
186 }
187 
188 void SmudgeTool::mouseReleaseEvent(QMouseEvent *event)
189 {
190  Layer* layer = mEditor->layers()->currentLayer();
191  if (layer == NULL) { return; }
192 
193  if (event->button() == Qt::LeftButton)
194  {
195  if (layer->type() == Layer::BITMAP)
196  {
197  drawStroke();
198  mScribbleArea->setAllDirty();
199  endStroke();
200  }
201  else if (layer->type() == Layer::VECTOR)
202  {
203  VectorImage *vectorImage = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
204  vectorImage->applySelectionTransformation();
205  mScribbleArea->selectionTransformation.reset();
206  for (int k = 0; k < mScribbleArea->vectorSelection.curve.size(); k++)
207  {
208  int curveNumber = mScribbleArea->vectorSelection.curve.at(k);
209  vectorImage->m_curves[curveNumber].smoothCurve();
210  }
211  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
212  }
213  }
214 }
215 
216 void SmudgeTool::mouseMoveEvent(QMouseEvent *event)
217 {
218  Layer* layer = mEditor->layers()->currentLayer();
219  if (layer == NULL) { return; }
220 
221  if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
222  {
223  if (event->buttons() & Qt::LeftButton) // the user is also pressing the mouse (dragging) {
224  {
225  if (layer->type() == Layer::BITMAP)
226  {
227  drawStroke();
228  }
229  else if (layer->type() == Layer::VECTOR)
230  {
231  if (event->modifiers() != Qt::ShiftModifier) // (and the user doesn't press shift)
232  {
233  // transforms the selection
234  mScribbleArea->selectionTransformation = QTransform().translate(mScribbleArea->mOffset.x(), mScribbleArea->mOffset.y());
235  ((LayerVector *)layer)->getLastVectorImageAtFrame( mEditor->currentFrame(), 0)->setSelectionTransformation(mScribbleArea->selectionTransformation);
236  }
237  }
238  }
239  else // the user is moving the mouse without pressing it
240  {
241  if (layer->type() == Layer::VECTOR)
242  {
243  mScribbleArea->mClosestVertices = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0)
244  ->getVerticesCloseTo( getCurrentPoint(), mScribbleArea->tol / mEditor->view()->scaling() );
245  }
246  }
247  mScribbleArea->update();
248  mScribbleArea->setAllDirty();
249  }
250 }
251 
252 void SmudgeTool::drawStroke()
253 {
254  if ( !mScribbleArea->isLayerPaintable() ) return;
255 
256  Layer* layer = mEditor->layers()->currentLayer();
257  if (layer == NULL) { return; }
258 
259  BitmapImage *targetImage = ((LayerBitmap *)layer)->getLastBitmapImageAtFrame(mEditor->currentFrame(), 0);
260  StrokeTool::drawStroke();
261  QList<QPointF> p = m_pStrokeManager->interpolateStroke();
262 
263  for (int i = 0; i < p.size(); i++)
264  {
265  p[ i ] = mEditor->view()->mapScreenToCanvas( p[ i ] );
266  }
267 
268  qreal opacity = 1.0;
269  qreal brushWidth = mCurrentWidth + 0.0 * properties.feather;
270  qreal offset = qMax(0.0, mCurrentWidth - 0.5 * properties.feather) / brushWidth;
271  //opacity = currentPressure; // todo: Probably not interesting?!
272  //brushWidth = brushWidth * opacity;
273 
274  BlitRect rect;
275  QPointF a = mLastBrushPoint;
276  QPointF b = getCurrentPoint();
277 
278 
279  if (toolMode == 0) // liquify hard (default)
280  {
281  qreal brushStep = 2;
282  qreal distance = QLineF(b, a).length()/2.0;
283  int steps = qRound(distance / brushStep);
284  int rad = qRound(brushWidth / 2.0) + 2;
285 
286  QPointF sourcePoint = mLastBrushPoint;
287  for (int i = 0; i < steps; i++)
288  {
289  QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance;
290  rect.extend(targetPoint.toPoint());
291  mScribbleArea->liquifyBrush( targetImage,
292  sourcePoint,
293  targetPoint,
294  brushWidth,
295  offset,
296  opacity);
297 
298  if (i == (steps - 1))
299  {
300  mLastBrushPoint = targetPoint;
301  }
302  sourcePoint = targetPoint;
303  mScribbleArea->paintBitmapBufferRect( rect );
304  mScribbleArea->refreshBitmap(rect, rad);
305  }
306  }
307  else // liquify smooth
308  {
309  qreal brushStep = 2.0;
310  qreal distance = QLineF(b, a).length();
311  int steps = qRound(distance / brushStep);
312  int rad = qRound(brushWidth / 2.0) + 2;
313 
314  QPointF sourcePoint = mLastBrushPoint;
315  for (int i = 0; i < steps; i++)
316  {
317  QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance;
318  rect.extend(targetPoint.toPoint());
319  mScribbleArea->blurBrush( targetImage,
320  sourcePoint,
321  targetPoint,
322  brushWidth,
323  offset,
324  opacity);
325 
326  if (i == (steps - 1))
327  {
328  mLastBrushPoint = targetPoint;
329  }
330  sourcePoint = targetPoint;
331  mScribbleArea->paintBitmapBufferRect( rect );
332  mScribbleArea->refreshBitmap(rect, rad);
333  }
334  }
335 }
Definition: layer.h:32