Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
pentool.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 
20 #include "layervector.h"
21 #include "colormanager.h"
22 #include "strokemanager.h"
23 #include "layermanager.h"
24 #include "pencilsettings.h"
25 #include "editor.h"
26 #include "scribblearea.h"
27 #include "blitrect.h"
28 
29 #include "pentool.h"
30 
31 
32 PenTool::PenTool( QObject *parent ) : StrokeTool( parent )
33 {
34 }
35 
36 void PenTool::loadSettings()
37 {
38  m_enabledProperties[WIDTH] = true;
39  m_enabledProperties[PRESSURE] = true;
40  m_enabledProperties[VECTORMERGE] = true;
41  m_enabledProperties[ANTI_ALIASING] = true;
42  m_enabledProperties[INTERPOLATION] = true;
43 
44  QSettings settings( PENCIL2D, PENCIL2D );
45 
46  properties.width = settings.value( "penWidth" ).toDouble();
47  properties.pressure = settings.value( "penPressure" ).toBool();
48  properties.invisibility = OFF;
49  properties.preserveAlpha = OFF;
50  properties.useAA = settings.value( "brushAA").toBool();
51  properties.inpolLevel = 0;
52 
53  // First run
54  if ( properties.width <= 0 )
55  {
56  setWidth(1.5);
57  setPressure(1);
58  }
59 
60  mCurrentWidth = properties.width;
61 }
62 
63 void PenTool::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("penWidth", width);
71  settings.sync();
72 }
73 
74 void PenTool::setPressure( const bool pressure )
75 {
76  // Set current property
77  properties.pressure = pressure;
78 
79  // Update settings
80  QSettings settings( PENCIL2D, PENCIL2D );
81  settings.setValue("penPressure", pressure);
82  settings.sync();
83 }
84 
85 void PenTool::setAA(const int AA )
86 {
87  // Set current property
88  properties.useAA = AA;
89 
90  // Update settings
91  QSettings settings( PENCIL2D, PENCIL2D );
92  settings.setValue("brushAA", AA);
93  settings.sync();
94 }
95 
96 void PenTool::setInpolLevel(const int level)
97 {
98  properties.inpolLevel = level;
99 
100  QSettings settings( PENCIL2D, PENCIL2D);
101  settings.setValue("lineInpol", level);
102  settings.sync();
103 }
104 
105 QCursor PenTool::cursor()
106 {
107  if ( mEditor->preference()->isOn( SETTING::TOOL_CURSOR ) )
108  {
109  return QCursor( QPixmap( ":icons/pen.png" ), -5, 0 );
110  }
111  return Qt::CrossCursor;
112 }
113 
114 void PenTool::adjustPressureSensitiveProperties( qreal pressure, bool mouseDevice )
115 {
116  mCurrentWidth = properties.width;
117 
118  if ( properties.pressure && !mouseDevice )
119  {
120  mCurrentPressure = pressure;
121  }
122  else
123  {
124  mCurrentPressure = 1.0;
125  }
126 }
127 
128 void PenTool::mousePressEvent( QMouseEvent *event )
129 {
130  if ( event->button() == Qt::LeftButton )
131  {
132  mEditor->backup( typeName() );
133  mScribbleArea->setAllDirty();
134  }
135 
136  mMouseDownPoint = getCurrentPoint();
137  mLastBrushPoint = getCurrentPoint();
138 
139  startStroke();
140 }
141 
142 void PenTool::mouseReleaseEvent( QMouseEvent *event )
143 {
144  if ( event->button() == Qt::LeftButton )
145  {
146  Layer* layer = mEditor->layers()->currentLayer();
147  if ( mScribbleArea->isLayerPaintable() )
148  {
149  qreal distance = QLineF( getCurrentPoint(), mMouseDownPoint ).length();
150  if (distance < 1)
151  {
152  paintAt(mMouseDownPoint);
153  }
154  else
155  {
156  drawStroke();
157  }
158  }
159 
160  if ( layer->type() == Layer::BITMAP ) {
161  paintBitmapStroke();
162  }
163  else if (layer->type() == Layer::VECTOR )
164  {
165  paintVectorStroke( layer );
166  }
167  }
168  endStroke();
169 }
170 
171 void PenTool::mouseMoveEvent( QMouseEvent *event )
172 {
173  Layer* layer = mEditor->layers()->currentLayer();
174  if ( layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR )
175  {
176  if ( event->buttons() & Qt::LeftButton )
177  {
178  drawStroke();
179  if (properties.inpolLevel != m_pStrokeManager->getInpolLevel()) {
180  m_pStrokeManager->setInpolLevel(properties.inpolLevel);
181  }
182  //qDebug() << "DrawStroke" << event->pos() ;
183  }
184  }
185 }
186 
187 // draw a single paint dab at the given location
188 void PenTool::paintAt( QPointF point )
189 {
190  qDebug() << "Made a single dab at " << point;
191  Layer* layer = mEditor->layers()->currentLayer();
192  if ( layer->type() == Layer::BITMAP )
193  {
194  mCurrentWidth = properties.width;
195  if (properties.pressure == true)
196  {
197  mCurrentWidth = properties.width;
198  }
199  qreal brushWidth = mCurrentWidth;
200 
201  BlitRect rect;
202 
203  rect.extend( point.toPoint() );
204  mScribbleArea->drawPen( QPoint( qRound(point.x() ), qRound(point.y() )),
205  brushWidth,
206  mEditor->color()->frontColor(),
207  properties.useAA);
208 
209  int rad = qRound( brushWidth ) / 2 + 2;
210 
211  mScribbleArea->refreshBitmap( rect, rad );
212  }
213 }
214 
215 void PenTool::drawStroke()
216 {
217  StrokeTool::drawStroke();
218  QList<QPointF> p = m_pStrokeManager->interpolateStroke();
219 
220  Layer* layer = mEditor->layers()->currentLayer();
221 
222  if ( layer->type() == Layer::BITMAP )
223  {
224  for ( int i = 0; i < p.size(); i++ )
225  {
226  p[ i ] = mEditor->view()->mapScreenToCanvas( p[ i ] );
227  }
228 
229  mCurrentWidth = properties.width;
230  if (properties.pressure == true)
231  {
232  mCurrentWidth = properties.width * mCurrentPressure;
233  }
234  qreal brushWidth = mCurrentWidth;
235 
236  // TODO: Make popup widget for less important properties,
237  // Eg. stepsize should be a slider.. will have fixed (0.3) value for now.
238  qreal brushStep = ( 0.5 * brushWidth );
239  brushStep = qMax( 1.0, brushStep );
240 
241  BlitRect rect;
242 
243  QPointF a = mLastBrushPoint;
244  QPointF b = getCurrentPoint();
245 
246  qreal distance = 4 * QLineF( b, a ).length();
247  int steps = qRound( distance / brushStep );
248 
249  for ( int i = 0; i < steps; i++ )
250  {
251  QPointF point = mLastBrushPoint + ( i + 1 ) * brushStep * ( getCurrentPoint() - mLastBrushPoint ) / distance;
252  rect.extend( point.toPoint() );
253  mScribbleArea->drawPen( QPoint( qRound(point.x() ), qRound(point.y() )),
254  brushWidth,
255  mEditor->color()->frontColor(),
256  properties.useAA );
257 
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  qreal brushWidth = 0;
272  brushWidth = properties.width;
273  if (properties.pressure == true) {
274  brushWidth = properties.width * mCurrentPressure;
275  }
276 
277  int rad = qRound( ( brushWidth / 2 + 2 ) * mEditor->view()->scaling() );
278 
279  QPen pen( mEditor->color()->frontColor(),
280  brushWidth * mEditor->view()->scaling(),
281  Qt::SolidLine,
282  Qt::RoundCap,
283  Qt::RoundJoin );
284 
285  if ( p.size() == 4 )
286  {
287  QPainterPath path( p[ 0 ] );
288  path.cubicTo( p[ 1 ],
289  p[ 2 ],
290  p[ 3 ] );
291  mScribbleArea->drawPath( path, pen, Qt::NoBrush, QPainter::CompositionMode_Source );
292  mScribbleArea->refreshVector( path.boundingRect().toRect(), rad );
293  }
294  }
295 }
296 
297 void PenTool::paintBitmapStroke()
298 {
299  mScribbleArea->paintBitmapBuffer();
300  mScribbleArea->setAllDirty();
301  mScribbleArea->clearBitmapBuffer();
302 }
303 
304 void PenTool::paintVectorStroke(Layer* layer)
305 {
306  // Clear the temporary pixel path
307  mScribbleArea->clearBitmapBuffer();
308  qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
309 
310  BezierCurve curve( mStrokePoints, mStrokePressures, tol );
311  curve.setWidth( properties.width );
312  curve.setFeather( properties.feather );
313  curve.setInvisibility( properties.invisibility );
314  curve.setVariableWidth( properties.pressure );
315  curve.setColourNumber( mEditor->color()->frontColorNumber() );
316 
317  auto pLayerVector = static_cast< LayerVector* >( layer );
318  VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
319  vectorImage->insertCurve( 0, curve, mEditor->view()->scaling(), false );
320 
321  mScribbleArea->setModified( mEditor->layers()->currentLayerIndex(), mEditor->currentFrame() );
322  mScribbleArea->setAllDirty();
323 }
Definition: layer.h:32