Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
penciltool.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 <QSettings>
19 #include <QPixmap>
20 #include <QMouseEvent>
21 
22 #include "layermanager.h"
23 #include "layervector.h"
24 #include "layerbitmap.h"
25 #include "colormanager.h"
26 #include "strokemanager.h"
27 #include "layermanager.h"
28 #include "editor.h"
29 #include "scribblearea.h"
30 #include "pencilsettings.h"
31 #include "blitrect.h"
32 
33 #include "penciltool.h"
34 
35 PencilTool::PencilTool( QObject* parent ) : StrokeTool( parent )
36 {
37 }
38 
39 
40 void PencilTool::loadSettings()
41 {
42  m_enabledProperties[WIDTH] = true;
43  m_enabledProperties[PRESSURE] = true;
44  m_enabledProperties[VECTORMERGE] = false;
45  m_enabledProperties[INTERPOLATION] = true;
46  m_enabledProperties[FILLCONTOUR] = true;
47 
48  QSettings settings( PENCIL2D, PENCIL2D );
49  properties.width = settings.value( "pencilWidth" ).toDouble();
50  properties.feather = 1;
51  properties.pressure = settings.value( "pencilPressure" ).toBool();
52  properties.inpolLevel = 0;
53  properties.useAA = -1;
54  properties.useFillContour = false;
55 
56  // properties.invisibility = 1;
57  // properties.preserveAlpha = 0;
58 
59  if ( properties.width <= 0 )
60  {
61  // setting the default value to 4
62  // seems to give great results with pressure on
63  setWidth( 4 );
64  setPressure( 1 );
65  }
66 
67 }
68 
69 void PencilTool::setWidth(const qreal width)
70 {
71  // Set current property
72  properties.width = width;
73 
74  // Update settings
75  QSettings settings( PENCIL2D, PENCIL2D );
76  settings.setValue("pencilWidth", width);
77  settings.sync();
78 }
79 
80 void PencilTool::setFeather( const qreal feather )
81 {
82  properties.feather = feather;
83 }
84 
85 void PencilTool::setInvisibility( const bool )
86 {
87  // force value
88  properties.invisibility = 1;
89 }
90 
91 void PencilTool::setPressure( const bool pressure )
92 {
93  // Set current property
94  properties.pressure = pressure;
95 
96  // Update settings
97  QSettings settings( PENCIL2D, PENCIL2D );
98  settings.setValue("pencilPressure", pressure);
99  settings.sync();
100 }
101 
102 void PencilTool::setPreserveAlpha( const bool preserveAlpha )
103 {
104  // force value
105  Q_UNUSED( preserveAlpha );
106  properties.preserveAlpha = 0;
107 }
108 
109 void PencilTool::setInpolLevel(const int level)
110 {
111  properties.inpolLevel = level;
112 
113  QSettings settings( PENCIL2D, PENCIL2D);
114  settings.setValue("lineInpol", level);
115  settings.sync();
116 }
117 
118 void PencilTool::setUseFillContour(const bool useFillContour)
119 {
120  properties.useFillContour = useFillContour;
121 
122  QSettings settings( PENCIL2D, PENCIL2D);
123  settings.setValue("FillContour", useFillContour);
124  settings.sync();
125 }
126 
127 QCursor PencilTool::cursor()
128 {
129  if ( mEditor->preference()->isOn( SETTING::TOOL_CURSOR ) )
130  {
131  return QCursor( QPixmap( ":icons/pencil2.png" ), 0, 16 );
132  }
133  return Qt::CrossCursor;
134 }
135 
136 void PencilTool::mousePressEvent( QMouseEvent *event )
137 {
138  mLastBrushPoint = getCurrentPoint();
139 
140  if ( event->button() == Qt::LeftButton )
141  {
142  mEditor->backup( typeName() );
143 
144  mScribbleArea->setAllDirty();
145  startStroke(); //start and appends first stroke
146 
147  //Layer *layer = m_pEditor->getCurrentLayer();
148 
149  if ( mEditor->layers()->currentLayer()->type() == Layer::BITMAP ) // in case of bitmap, first pixel(mouseDown) is drawn
150  {
151  drawStroke();
152  }
153  else if ( mEditor->layers()->currentLayer()->type() == Layer::VECTOR )
154  {
155  if ( !mEditor->preference()->isOn(SETTING::INVISIBLE_LINES) )
156  {
157  mScribbleArea->toggleThinLines();
158  }
159  }
160  }
161 
162  mMouseDownPoint = getCurrentPoint();
163  mLastBrushPoint = getCurrentPoint();
164 }
165 
166 void PencilTool::mouseMoveEvent( QMouseEvent *event )
167 {
168  Layer* layer = mEditor->layers()->currentLayer();
169  if ( layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR )
170  {
171  if ( event->buttons() & Qt::LeftButton )
172  {
173  drawStroke();
174  if (properties.inpolLevel != m_pStrokeManager->getInpolLevel()) {
175  m_pStrokeManager->setInpolLevel(properties.inpolLevel);
176  }
177  }
178  }
179 }
180 
181 void PencilTool::mouseReleaseEvent( QMouseEvent *event )
182 {
183  if ( event->button() == Qt::LeftButton )
184  {
185  Layer* layer = mEditor->layers()->currentLayer();
186  if ( mScribbleArea->isLayerPaintable() )
187  {
188  qreal distance = QLineF( getCurrentPoint(), mMouseDownPoint ).length();
189  if (distance < 1)
190  {
191  paintAt(mMouseDownPoint);
192  }
193  else
194  {
195  drawStroke();
196  }
197  }
198 
199  if ( layer->type() == Layer::BITMAP ) {
200  paintBitmapStroke();
201  }
202  else if (layer->type() == Layer::VECTOR )
203  {
204  paintVectorStroke( layer );
205  }
206  }
207  endStroke();
208 }
209 
210 void PencilTool::adjustPressureSensitiveProperties( qreal pressure, bool mouseDevice )
211 {
212  mCurrentWidth = properties.width;
213 
214  if ( properties.pressure && !mouseDevice )
215  {
216  mCurrentPressure = pressure;
217  }
218  else
219  {
220  mCurrentPressure = 1.0;
221  }
222 }
223 
224 // draw a single paint dab at the given location
225 void PencilTool::paintAt( QPointF point )
226 {
227  qDebug() << "Made a single dab at " << point;
228  Layer* layer = mEditor->layers()->currentLayer();
229  if ( layer->type() == Layer::BITMAP )
230  {
231  qreal opacity = 1.0;
232  mCurrentWidth = properties.width;
233  qreal brushWidth = mCurrentWidth;
234 
235  BlitRect rect;
236 
237  rect.extend( point.toPoint() );
238  mScribbleArea->drawPencil( QPoint( qRound(point.x() ), qRound(point.y() )),
239  brushWidth,
240  mEditor->color()->frontColor(),
241  opacity);
242 
243  int rad = qRound( brushWidth ) / 2 + 2;
244  mScribbleArea->refreshBitmap( rect, rad );
245  }
246 }
247 
248 
249 void PencilTool::drawStroke()
250 {
251  StrokeTool::drawStroke();
252  QList<QPointF> p = m_pStrokeManager->interpolateStroke();
253 
254  Layer* layer = mEditor->layers()->currentLayer();
255 
256  if ( layer->type() == Layer::BITMAP )
257  {
258  qreal opacity = 1.0;
259  mCurrentWidth = properties.width;
260  if (properties.pressure == true) {
261  opacity = mCurrentPressure / 2;
262  mCurrentWidth = properties.width * mCurrentPressure;
263  }
264  qreal brushWidth = mCurrentWidth;
265 
266  qreal brushStep = (0.5 * brushWidth);
267  brushStep = qMax( 1.0, brushStep );
268 
269  BlitRect rect;
270 
271  QPointF a = mLastBrushPoint;
272  QPointF b = getCurrentPoint();
273 
274  qreal distance = 4 * QLineF( b, a ).length();
275  int steps = qRound( distance / brushStep );
276 
277  for ( int i = 0; i < steps; i++ )
278  {
279  QPointF point = mLastBrushPoint + ( i + 1 ) * brushStep * ( getCurrentPoint() - mLastBrushPoint ) / distance;
280  rect.extend( point.toPoint() );
281  mScribbleArea->drawPencil( QPoint( qRound(point.x() ), qRound(point.y() )),
282  brushWidth,
283  mEditor->color()->frontColor(),
284  opacity );
285 
286  if ( i == ( steps - 1 ) )
287  {
288  mLastBrushPoint = getCurrentPoint();
289  }
290  }
291 
292  int rad = qRound( brushWidth ) / 2 + 2;
293 
294  mScribbleArea->paintBitmapBufferRect( rect );
295  mScribbleArea->refreshBitmap( rect, rad );
296 
297  }
298  else if ( layer->type() == Layer::VECTOR )
299  {
300  QPen pen( mEditor->color()->frontColor(),
301  1,
302  Qt::DotLine,
303  Qt::RoundCap,
304  Qt::RoundJoin );
305 
306  int rad = qRound( ( properties.width / 2 + 2 ) * mEditor->view()->scaling() );
307 
308  if ( p.size() == 4 ) {
309  QSizeF size( 2, 2 );
310  QPainterPath path( p[ 0 ] );
311  path.cubicTo( p[ 1 ],
312  p[ 2 ],
313  p[ 3 ] );
314  mScribbleArea->drawPath( path, pen, Qt::NoBrush, QPainter::CompositionMode_Source );
315  mScribbleArea->refreshVector( path.boundingRect().toRect(), rad );
316  }
317  }
318 }
319 
320 
321 void PencilTool::paintBitmapStroke()
322 {
323  mScribbleArea->paintBitmapBuffer();
324  mScribbleArea->setAllDirty();
325  mScribbleArea->clearBitmapBuffer();
326 }
327 
328 void PencilTool::paintVectorStroke(Layer* layer)
329 {
330  // Clear the temporary pixel path
331  mScribbleArea->clearBitmapBuffer();
332  qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
333 
334  BezierCurve curve( mStrokePoints, mStrokePressures, tol );
335  curve.setWidth( 0 );
336  curve.setFeather( 0 );
337  curve.setInvisibility( true );
338  curve.setVariableWidth( false );
339  curve.setColourNumber( mEditor->color()->frontColorNumber() );
340  VectorImage* vectorImage = ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
341 
342  vectorImage->addCurve( curve, qAbs( mEditor->view()->scaling() ), properties.vectorMergeEnabled );
343 
344  if (properties.useFillContour == true)
345  {
346  vectorImage->fillPath( mStrokePoints,
347  mEditor->color()->frontColorNumber(),
348  10.0 / mEditor->view()->scaling() );
349  }
350  mScribbleArea->setModified( mEditor->layers()->currentLayerIndex(), mEditor->currentFrame() );
351  mScribbleArea->setAllDirty();
352 }
Definition: layer.h:32