Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
brushtool.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 "brushtool.h"
19 
20 #include <cmath>
21 #include <QSettings>
22 #include <QPixmap>
23 #include <QPainter>
24 
25 #include "layervector.h"
26 #include "layer.h"
27 #include "editor.h"
28 #include "pencilsettings.h"
29 #include "colormanager.h"
30 #include "strokemanager.h"
31 #include "layermanager.h"
32 #include "scribblearea.h"
33 #include "blitrect.h"
34 
35 
36 
37 BrushTool::BrushTool( QObject *parent ) :
38 StrokeTool( parent )
39 {
40 }
41 
42 ToolType BrushTool::type()
43 {
44  return BRUSH;
45 }
46 
47 void BrushTool::loadSettings()
48 {
49  m_enabledProperties[WIDTH] = true;
50  m_enabledProperties[FEATHER] = true;
51  m_enabledProperties[USEFEATHER] = true;
52  m_enabledProperties[PRESSURE] = true;
53  m_enabledProperties[INVISIBILITY] = true;
54  m_enabledProperties[INTERPOLATION] = true;
55  m_enabledProperties[ANTI_ALIASING] = true;
56 
57  QSettings settings( PENCIL2D, PENCIL2D );
58 
59  properties.width = settings.value( "brushWidth" ).toDouble();
60  properties.feather = settings.value( "brushFeather", 15.0 ).toDouble();
61  properties.useFeather = settings.value( "brushUseFeather", true ).toBool();
62  properties.pressure = settings.value( "brushPressure", false ).toBool();
63  properties.invisibility = settings.value("brushInvisibility", true).toBool();
64  properties.preserveAlpha = OFF;
65  properties.inpolLevel = 0;
66  properties.useAA = 1;
67 
68  if (properties.useFeather == true) {
69  properties.useAA = -1;
70  }
71 
72  // First run
73  //
74  if ( properties.width <= 0 )
75  {
76  setWidth(15);
77  }
78 
79  if ( std::isnan( properties.feather ) )
80  {
81  setFeather( 15 );
82  }
83 }
84 
85 void BrushTool::setWidth(const qreal width)
86 {
87  // Set current property
88  properties.width = width;
89 
90  // Update settings
91  QSettings settings( PENCIL2D, PENCIL2D );
92  settings.setValue("brushWidth", width);
93  settings.sync();
94 }
95 
96 void BrushTool::setUseFeather( const bool usingFeather )
97 {
98  // Set current property
99  properties.useFeather = usingFeather;
100 
101  // Update settings
102  QSettings settings( PENCIL2D, PENCIL2D );
103  settings.setValue("brushUseFeather", usingFeather);
104  settings.sync();
105 }
106 
107 void BrushTool::setFeather( const qreal feather )
108 {
109  // Set current property
110  properties.feather = feather;
111 
112  // Update settings
113  QSettings settings( PENCIL2D, PENCIL2D );
114  settings.setValue("brushFeather", feather);
115  settings.sync();
116 }
117 
118 void BrushTool::setInvisibility( const bool invisibility )
119 {
120  // force value
121  properties.invisibility = invisibility;
122 
123  QSettings settings ( PENCIL2D, PENCIL2D );
124  settings.setValue("brushInvisibility",invisibility);
125  settings.sync();
126 }
127 
128 void BrushTool::setPressure( const bool pressure )
129 {
130  // Set current property
131  properties.pressure = pressure;
132 
133  // Update settings
134  QSettings settings( PENCIL2D, PENCIL2D );
135  settings.setValue("brushPressure", pressure);
136  settings.sync();
137 }
138 
139 void BrushTool::setInpolLevel(const int level)
140 {
141  properties.inpolLevel = level;
142 
143  QSettings settings( PENCIL2D, PENCIL2D);
144  settings.setValue("lineInpol", level);
145  settings.sync();
146 }
147 
148 void BrushTool::setAA( const int AA )
149 {
150  // Set current property
151  properties.useAA = AA;
152 
153  // Update settings
154  QSettings settings( PENCIL2D, PENCIL2D );
155  settings.setValue("brushAA", AA);
156  settings.sync();
157 }
158 
159 QCursor BrushTool::cursor()
160 {
161  if ( mEditor->preference()->isOn( SETTING::TOOL_CURSOR ) )
162  {
163  return QCursor( QPixmap( ":icons/brush.png" ), 0, 13 );
164  }
165  return Qt::CrossCursor;
166 }
167 
168 void BrushTool::adjustPressureSensitiveProperties( qreal pressure, bool mouseDevice )
169 {
170 // Layer* layer = mEditor->layers()->currentLayer();
171 
172 // // In Bitmap mode, the brush tool pressure only handles opacity while the Pen tool
173 // // only handles size. Pencil tool handles both.
174 
175 // QColor currentColor = mEditor->color()->frontColor();
176 // currentPressuredColor = currentColor;
177 
178 // if ( layer->type() == Layer::BITMAP && properties.pressure && !mouseDevice )
179 // {
180 // currentPressuredColor.setAlphaF( currentColor.alphaF() * pressure * pressure );
181 // }
182 
183 // mCurrentWidth = properties.width;
184 
185  if ( properties.pressure && !mouseDevice )
186  {
187  mCurrentPressure = pressure;
188  }
189  else
190  {
191  mCurrentPressure = 1.0;
192  }
193 }
194 
195 void BrushTool::mousePressEvent( QMouseEvent *event )
196 {
197  if ( event->button() == Qt::LeftButton )
198  {
199  mEditor->backup( typeName() );
200  mScribbleArea->setAllDirty();
201  }
202 
203  mMouseDownPoint = getCurrentPoint();
204 
205  mLastBrushPoint = getCurrentPoint();
206  startStroke();
207 
208  if ( !mEditor->preference()->isOn(SETTING::INVISIBLE_LINES) )
209  {
210  mScribbleArea->toggleThinLines();
211  }
212 
213 }
214 
215 void BrushTool::mouseReleaseEvent( QMouseEvent *event )
216 {
217  if ( event->button() == Qt::LeftButton )
218  {
219  Layer* layer = mEditor->layers()->currentLayer();
220  if ( mScribbleArea->isLayerPaintable() )
221  {
222  qreal distance = QLineF( getCurrentPoint(), mMouseDownPoint ).length();
223  if (distance < 1)
224  {
225  paintAt(mMouseDownPoint);
226  }
227  else
228  {
229  drawStroke();
230  }
231  }
232 
233  if ( layer->type() == Layer::BITMAP ) {
234  paintBitmapStroke();
235  }
236  else if (layer->type() == Layer::VECTOR )
237  {
238  paintVectorStroke();
239  }
240  }
241  endStroke();
242 }
243 
244 void BrushTool::mouseMoveEvent( QMouseEvent *event )
245 {
246  if ( mScribbleArea->isLayerPaintable() )
247  {
248  if ( event->buttons() & Qt::LeftButton )
249  {
250  drawStroke();
251  if (properties.inpolLevel != m_pStrokeManager->getInpolLevel()) {
252  m_pStrokeManager->setInpolLevel(properties.inpolLevel);
253  }
254  }
255  }
256 }
257 
258 // draw a single paint dab at the given location
259 void BrushTool::paintAt( QPointF point )
260 {
261  qDebug() << "Made a single dab at " << point;
262  Layer* layer = mEditor->layers()->currentLayer();
263  if ( layer->type() == Layer::BITMAP )
264  {
265  qreal opacity = 1.0;
266  mCurrentWidth = properties.width;
267  qreal brushWidth = mCurrentWidth;
268 
269  BlitRect rect;
270 
271  rect.extend( point.toPoint() );
272  mScribbleArea->drawBrush( QPoint( qRound(point.x() ), qRound(point.y() )),
273  brushWidth,
274  properties.feather,
275  mEditor->color()->frontColor(),
276  opacity,
277  properties.useFeather,
278  properties.useAA );
279 
280  int rad = qRound( brushWidth ) / 2 + 2;
281  mScribbleArea->refreshBitmap( rect, rad );
282  }
283 }
284 
285 void BrushTool::drawStroke()
286 {
287  StrokeTool::drawStroke();
288  QList<QPointF> p = m_pStrokeManager->interpolateStroke();
289 
290  Layer* layer = mEditor->layers()->currentLayer();
291 
292  if ( layer->type() == Layer::BITMAP )
293  {
294  for ( int i = 0; i < p.size(); i++ )
295  {
296  p[ i ] = mEditor->view()->mapScreenToCanvas( p[ i ] );
297  }
298 
299  qreal opacity = 1.0;
300  if (properties.pressure == true) {
301  opacity = mCurrentPressure / 2;
302  }
303 
304  mCurrentWidth = properties.width;
305  qreal brushWidth = mCurrentWidth;
306 
307  qreal brushStep = (0.5 * brushWidth);
308  brushStep = qMax( 1.0, brushStep );
309 
310  BlitRect rect;
311 
312  QPointF a = mLastBrushPoint;
313  QPointF b = getCurrentPoint();
314 
315  qreal distance = 4 * QLineF( b, a ).length();
316  int steps = qRound( distance / brushStep);
317 
318  for ( int i = 0; i < steps; i++ )
319  {
320  QPointF point = mLastBrushPoint + ( i + 1 ) * brushStep * ( getCurrentPoint() - mLastBrushPoint ) / distance;
321 
322  rect.extend( point.toPoint() );
323  mScribbleArea->drawBrush( QPoint( qRound(point.x() ),qRound(point.y() )),
324  brushWidth,
325  properties.feather,
326  mEditor->color()->frontColor(),
327  opacity,
328  properties.useFeather,
329  properties.useAA );
330  if ( i == ( steps - 1 ) )
331  {
332  mLastBrushPoint = getCurrentPoint();
333  }
334  }
335 
336  int rad = qRound( brushWidth / 2 + 2);
337 
338  mScribbleArea->paintBitmapBufferRect( rect );
339  mScribbleArea->refreshBitmap( rect, rad );
340 
341  // Line visualizer
342  // for debugging
343 // QPainterPath tempPath;
344 
345 // QPointF mappedMousePos = mEditor->view()->mapScreenToCanvas(m_pStrokeManager->getMousePos());
346 // tempPath.moveTo(getCurrentPoint());
347 // tempPath.lineTo(mappedMousePos);
348 
349 // QPen pen( Qt::black,
350 // 1,
351 // Qt::SolidLine,
352 // Qt::RoundCap,
353 // Qt::RoundJoin );
354 // mScribbleArea->drawPolyline(tempPath, pen, true);
355 
356  }
357  else if ( layer->type() == Layer::VECTOR )
358  {
359  qreal brushWidth = 0;
360  if (properties.pressure ) {
361  brushWidth = properties.width * mCurrentPressure;
362  }
363  else {
364  brushWidth = properties.width;
365  }
366 
367  int rad = qRound( ( brushWidth / 2 + 2 ) * mEditor->view()->scaling() );
368 
369  QPen pen( mEditor->color()->frontColor(),
370  brushWidth * mEditor->view()->scaling(),
371  Qt::SolidLine,
372  Qt::RoundCap,
373  Qt::RoundJoin );
374 
375  if ( p.size() == 4 )
376  {
377  QPainterPath path( p[ 0 ] );
378  path.cubicTo( p[ 1 ],
379  p[ 2 ],
380  p[ 3 ] );
381 
382  mScribbleArea->drawPath( path, pen, Qt::NoBrush, QPainter::CompositionMode_Source );
383  mScribbleArea->refreshVector( path.boundingRect().toRect(), rad );
384  }
385  }
386 }
387 
388 void BrushTool::paintBitmapStroke()
389 {
390  mScribbleArea->paintBitmapBuffer();
391  mScribbleArea->setAllDirty();
392  mScribbleArea->clearBitmapBuffer();
393 }
394 
395 // This function uses the points from DrawStroke
396 // and turns them into vector lines.
397 void BrushTool::paintVectorStroke()
398 {
399  Layer* layer = mEditor->layers()->currentLayer();
400 
401  if ( layer->type() == Layer::VECTOR && mStrokePoints.size() > -1 )
402  {
403 
404  // Clear the temporary pixel path
405  mScribbleArea->clearBitmapBuffer();
406  qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
407 
408  BezierCurve curve( mStrokePoints, mStrokePressures, tol );
409  curve.setWidth( properties.width );
410  curve.setFeather( properties.feather );
411  curve.setInvisibility( properties.invisibility );
412  curve.setVariableWidth( properties.pressure );
413  curve.setColourNumber( mEditor->color()->frontColorNumber() );
414 
415  auto pLayerVector = static_cast< LayerVector* >( layer );
416  VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
417  vectorImage->insertCurve( 0, curve, mEditor->view()->scaling(), false );
418 
419  mScribbleArea->setModified( mEditor->layers()->currentLayerIndex(), mEditor->currentFrame() );
420  mScribbleArea->setAllDirty();
421  }
422 }
Definition: layer.h:32