Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
scribblearea.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 "scribblearea.h"
19 
20 #include <cmath>
21 #include <QScopedPointer>
22 #include <QMessageBox>
23 #include <QPixmapCache>
24 
25 #include "beziercurve.h"
26 #include "object.h"
27 #include "editor.h"
28 #include "layerbitmap.h"
29 #include "layervector.h"
30 #include "layercamera.h"
31 #include "bitmapimage.h"
32 #include "pencilsettings.h"
33 #include "toolmanager.h"
34 #include "strokemanager.h"
35 #include "layermanager.h"
36 #include "playbackmanager.h"
37 
38 #define round(f) ((int)(f + 0.5))
39 
40 
41 ScribbleArea::ScribbleArea( QWidget* parent ) : QWidget( parent ),
42 mLog( "ScribbleArea" )
43 {
44  setObjectName( "ScribbleArea" );
45 
46  // Qt::WA_StaticContents ensure that the widget contents are rooted to the top-left corner
47  // and don't change when the widget is resized.
48  setAttribute( Qt::WA_StaticContents );
49 
50  mStrokeManager.reset( new StrokeManager );
51 }
52 
53 ScribbleArea::~ScribbleArea()
54 {
55  delete mBufferImg;
56 }
57 
58 bool ScribbleArea::init()
59 {
60  mPrefs = mEditor->preference();
61 
62  connect( mPrefs, &PreferenceManager::optionChanged, this, &ScribbleArea::settingUpdated );
63 
64  int curveSmoothingLevel = mPrefs->getInt(SETTING::CURVE_SMOOTHING);
65  mCurveSmoothingLevel = curveSmoothingLevel / 20.0; // default value is 1.0
66 
67  mQuickSizing = mPrefs->isOn( SETTING::QUICK_SIZING );
68  mMakeInvisible = false;
69  somethingSelected = false;
70 
71  mIsSimplified = mPrefs->isOn( SETTING::OUTLINES );
72  mMultiLayerOnionSkin = mPrefs->isOn( SETTING::MULTILAYER_ONION );
73 
74  mShowAllLayers = 1;
75 
76  mBufferImg = new BitmapImage;
77 
78  QRect newSelection( QPoint( 0, 0 ), QSize( 0, 0 ) );
79  mySelection = newSelection;
80  myTransformedSelection = newSelection;
81  myTempTransformedSelection = newSelection;
82  mOffset.setX( 0 );
83  mOffset.setY( 0 );
84  selectionTransformation.reset();
85 
86  updateCanvasCursor();
87 
88  tol = 7.0;
89 
90  setMouseTracking( true ); // reacts to mouse move events, even if the button is not pressed
91 
92  mDebugRect = QRectF( 0, 0, 0, 0 );
93 
94  setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ) );
95 
96  QPixmapCache::setCacheLimit( 100 * 1024 ); // unit is kb, so it's 100MB cache
97 
98  int nLength = mEditor->layers()->projectLength();
99  mPixmapCacheKeys.resize( std::max( nLength, 240 ) );
100 
101  mNeedUpdateAll = false;
102 
103  return true;
104 }
105 
106 void ScribbleArea::settingUpdated(SETTING setting)
107 {
108  switch ( setting )
109  {
110  case SETTING::CURVE_SMOOTHING:
111  setCurveSmoothing(mPrefs->getInt(SETTING::CURVE_SMOOTHING));
112  break;
113  case SETTING::TOOL_CURSOR:
114  updateToolCursor();
115  break;
116  case SETTING::ONION_PREV_FRAMES_NUM:
117  case SETTING::ONION_NEXT_FRAMES_NUM:
118  case SETTING::ONION_MIN_OPACITY:
119  case SETTING::ONION_MAX_OPACITY:
120  case SETTING::ANTIALIAS:
121  case SETTING::GRID:
122  case SETTING::GRID_SIZE:
123  case SETTING::PREV_ONION:
124  case SETTING::NEXT_ONION:
125  case SETTING::ONION_BLUE:
126  case SETTING::ONION_RED:
127  case SETTING::INVISIBLE_LINES:
128  case SETTING::OUTLINES:
129  updateAllFrames();
130  break;
131  case SETTING::QUICK_SIZING:
132  mQuickSizing = mPrefs->isOn( SETTING::QUICK_SIZING );
133  break;
134  case SETTING::MULTILAYER_ONION:
135  mMultiLayerOnionSkin = mPrefs->isOn( SETTING::MULTILAYER_ONION );
136  updateAllFrames();
137  default:
138  break;
139  }
140 
141 }
142 
143 void ScribbleArea::updateToolCursor()
144 {
145  setCursor( currentTool()->cursor() );
146  updateCanvasCursor();
147  updateAllFrames();
148 }
149 
150 void ScribbleArea::setCurveSmoothing( int newSmoothingLevel )
151 {
152  mCurveSmoothingLevel = newSmoothingLevel / 20.0;
153  updateAllFrames();
154 }
155 
156 void ScribbleArea::setEffect( SETTING e, bool isOn )
157 {
158  mPrefs->set(e, isOn);
159  updateAllFrames();
160 }
161 
162 /************************************************************************************/
163 // update methods
164 
165 void ScribbleArea::updateCurrentFrame()
166 {
167  updateFrame( mEditor->currentFrame() );
168 }
169 
170 void ScribbleArea::updateFrame( int frame )
171 {
172  int frameNumber = mEditor->layers()->LastFrameAtFrame( frame );
173 
174  Q_ASSERT( frame >= 0 );
175  if ( mPixmapCacheKeys.size() <= static_cast< unsigned >( frame ) )
176  {
177  mPixmapCacheKeys.resize( frame + 10 ); // a buffer
178  }
179 
180  QPixmapCache::remove( mPixmapCacheKeys[ frameNumber ] );
181  mPixmapCacheKeys[ frameNumber] = QPixmapCache::Key();
182 
183  update();
184 }
185 
186 void ScribbleArea::updateAllFrames()
187 {
188  QPixmapCache::clear();
189  std::fill( mPixmapCacheKeys.begin(), mPixmapCacheKeys.end(), QPixmapCache::Key() );
190 
191  update();
192  mNeedUpdateAll = false;
193 }
194 
195 void ScribbleArea::updateAllVectorLayersAtCurrentFrame()
196 {
197  updateAllVectorLayersAt( mEditor->currentFrame() );
198 }
199 
200 void ScribbleArea::updateAllVectorLayersAt( int frameNumber )
201 {
202  for ( int i = 0; i < mEditor->object()->getLayerCount(); i++ )
203  {
204  Layer *layer = mEditor->object()->getLayer( i );
205  if ( layer->type() == Layer::VECTOR )
206  {
207  auto vecLayer = static_cast< LayerVector* >( layer );
208  vecLayer->getLastVectorImageAtFrame( frameNumber, 0 )->modification();
209  }
210  }
211  updateFrame( mEditor->currentFrame() );
212 }
213 
214 void ScribbleArea::updateAllVectorLayers()
215 {
216  for ( int i = 0; i < mEditor->object()->getLayerCount(); i++ )
217  {
218  Layer *layer = mEditor->object()->getLayer( i );
219  if ( layer->type() == Layer::VECTOR )
220  {
221  //( ( LayerVector * )layer )->setModified( true );
222  }
223  }
224  updateAllFrames();
225 }
226 
227 void ScribbleArea::setModified( int layerNumber, int frameNumber )
228 {
229  Layer *layer = mEditor->object()->getLayer( layerNumber );
230  if ( layer->type() == Layer::VECTOR )
231  {
232  layer->setModified( frameNumber, true );
233  }
234  else if ( layer->type() == Layer::BITMAP )
235  {
236  layer->setModified( frameNumber, true );
237  }
238 
239  emit modification( layerNumber );
240 
241  updateAllFrames();
242 }
243 
244 /************************************************************************/
245 /* key event handlers */
246 /************************************************************************/
247 
248 void ScribbleArea::keyPressEvent( QKeyEvent *event )
249 {
250  // Don't handle this event on auto repeat
251  //
252  if (event->isAutoRepeat()) {
253  return;
254  }
255 
256  mKeyboardInUse = true;
257 
258  if ( mMouseInUse ){ return; } // prevents shortcuts calls while drawing
259 
260  if ( instantTool ){ return; } // prevents shortcuts calls while using instant tool
261 
262 
263  if ( currentTool()->keyPressEvent( event ) )
264  {
265  // has been handled by tool
266  return;
267  }
268 
269  // --- fixed control key shortcuts ---
270  if ( event->modifiers() == ( Qt::ControlModifier | Qt::ShiftModifier ) )
271  {
272  setTemporaryTool( ERASER );
273  return;
274  }
275 
276  // ---- fixed normal keys ----
277  switch ( event->key() )
278  {
279  case Qt::Key_Right:
280  if ( somethingSelected )
281  {
282  myTempTransformedSelection.translate( 1, 0 );
283  myTransformedSelection = myTempTransformedSelection;
284  calculateSelectionTransformation();
285  paintTransformedSelection();
286  }
287  else
288  {
289  mEditor->scrubForward();
290  event->ignore();
291  }
292  break;
293  case Qt::Key_Left:
294  if ( somethingSelected )
295  {
296  myTempTransformedSelection.translate( -1, 0 );
297  myTransformedSelection = myTempTransformedSelection;
298  calculateSelectionTransformation();
299  paintTransformedSelection();
300  }
301  else
302  {
303  mEditor->scrubBackward();
304  event->ignore();
305  }
306  break;
307  case Qt::Key_Up:
308  if ( somethingSelected )
309  {
310  myTempTransformedSelection.translate( 0, -1 );
311  myTransformedSelection = myTempTransformedSelection;
312  calculateSelectionTransformation();
313  paintTransformedSelection();
314  }
315  else
316  {
317  mEditor->layers()->gotoPreviouslayer();
318  event->ignore();
319  }
320  break;
321  case Qt::Key_Down:
322  if ( somethingSelected )
323  {
324  myTempTransformedSelection.translate( 0, 1 );
325  myTransformedSelection = myTempTransformedSelection;
326  calculateSelectionTransformation();
327  paintTransformedSelection();
328  }
329  else
330  {
331  mEditor->layers()->gotoNextLayer();
332  event->ignore();
333  }
334  break;
335  case Qt::Key_Return:
336  if ( somethingSelected )
337  {
338  applyTransformedSelection();
339  paintTransformedSelection();
340  deselectAll();
341  }
342  else
343  {
344  event->ignore();
345  }
346  break;
347  case Qt::Key_Escape:
348  if ( somethingSelected )
349  {
350  deselectAll();
351  applyTransformedSelection();
352  }
353  break;
354  case Qt::Key_Backspace:
355  if ( somethingSelected )
356  {
357  deleteSelection();
358  deselectAll();
359  }
360  break;
361  case Qt::Key_Space:
362  setTemporaryTool( HAND ); // just call "setTemporaryTool()" to activate temporarily any tool
363  break;
364  default:
365  event->ignore();
366  }
367 }
368 
369 void ScribbleArea::keyReleaseEvent( QKeyEvent *event )
370 {
371  // Don't handle this event on auto repeat
372  //
373  if (event->isAutoRepeat()) {
374  return;
375  }
376 
377  mKeyboardInUse = false;
378 
379  if ( mMouseInUse ) { return; }
380 
381  if ( instantTool ) // temporary tool
382  {
383  currentTool()->keyReleaseEvent( event );
384  setPrevTool();
385  return;
386  }
387  if ( currentTool()->keyReleaseEvent( event ) )
388  {
389  // has been handled by tool
390  return;
391  }
392 }
393 
394 /************************************************************************************/
395 // mouse and tablet event handlers
396 void ScribbleArea::wheelEvent( QWheelEvent* event )
397 {
398  QPoint pixels = event->pixelDelta();
399  QPoint angle = event->angleDelta();
400  if ( !pixels.isNull() )
401  {
402  float delta = pixels.y();
403  if(delta < 0)
404  {
405  mEditor->view()->scaleDown();
406  }
407  else
408  {
409  mEditor->view()->scaleUp();
410  }
411  }
412  else if ( !angle.isNull() )
413  {
414  float delta = angle.y();
415  if(delta < 0)
416  {
417  mEditor->view()->scaleDown();
418  }
419  else
420  {
421  mEditor->view()->scaleUp();
422  }
423  }
424  event->accept();
425 }
426 
427 void ScribbleArea::tabletEvent( QTabletEvent *event )
428 {
429  //qDebug() << "Device" << event->device() << "Pointer type" << event->pointerType();
430  mStrokeManager->tabletEvent( event );
431 
432  // Some tablets return "NoDevice" and Cursor.
433  if (event->device() == QTabletEvent::NoDevice) {
434  currentTool()->adjustPressureSensitiveProperties( mStrokeManager->getPressure(),
435  false );
436  } else {
437  currentTool()->adjustPressureSensitiveProperties( mStrokeManager->getPressure(),
438  event->pointerType() == QTabletEvent::Cursor );
439  }
440 
441  if ( event->pointerType() == QTabletEvent::Eraser )
442  {
443  editor()->tools()->tabletSwitchToEraser();
444  }
445  else {
446  editor()->tools()->tabletRestorePrevTool();
447  }
448  event->ignore(); // indicates that the tablet event is not accepted yet, so that it is propagated as a mouse event)
449 }
450 
451 bool ScribbleArea::isLayerPaintable() const
452 {
453  if ( !areLayersSane() )
454  return false;
455 
456  Layer* layer = mEditor->layers()->currentLayer();
457  return layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR;
458 }
459 
460 bool ScribbleArea::areLayersSane() const
461 {
462  Layer* layer = mEditor->layers()->currentLayer();
463  // ---- checks ------
464  if ( layer == NULL ) { return false; }
465  if ( layer->type() == Layer::VECTOR )
466  {
467  VectorImage *vectorImage = ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
468  if ( vectorImage == NULL ) { return false; }
469  }
470  if ( layer->type() == Layer::BITMAP )
471  {
472  BitmapImage *bitmapImage = ( ( LayerBitmap * )layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 );
473  if ( bitmapImage == NULL ) { return false; }
474  }
475  // ---- end checks ------
476 
477  return true;
478 }
479 
480 void ScribbleArea::mousePressEvent( QMouseEvent* event )
481 {
482  mMouseInUse = true;
483 
484  mStrokeManager->mousePressEvent( event );
485 
486  mUsePressure = currentTool()->properties.pressure;
487 
488  if ( !( mStrokeManager->isTabletInUse() && currentTool()->properties.pressure ) )
489  {
490  mStrokeManager->setPressure( 1.0 );
491  currentTool()->adjustPressureSensitiveProperties( 1.0, true );
492  }
493 
494  //----------------code for starting hand tool when middle mouse is pressed
495  if ( event->buttons() & Qt::MidButton )
496  {
497  //qDebug() << "Hand Start " << event->pos();
498  mPrevTemporalToolType = currentTool()->type();
499  editor()->tools()->setCurrentTool( HAND );
500  }
501  else if ( event->button() == Qt::LeftButton ) // if the user is pressing the left or right button
502  {
503  mLastPixel = mStrokeManager->getLastPressPixel();
504  mLastPoint = mEditor->view()->mapScreenToCanvas( mLastPixel );
505  }
506 
507  // ----- assisted tool adjusment -- todo: simplify this
508  if ( event->button() == Qt::LeftButton && mQuickSizing)
509  {
510  if ( ( event->modifiers() == Qt::ShiftModifier ) && ( currentTool()->properties.width > -1 ) )
511  {
512  //adjust width if not locked
513  currentTool()->startAdjusting( WIDTH, 1 );
514  return;
515  }
516  if ( ( event->modifiers() == Qt::ControlModifier ) && ( currentTool()->properties.feather > -1 ) )
517  {
518  //adjust feather if not locked
519  currentTool()->startAdjusting( FEATHER, 1 );
520  return;
521  }
522  if ( ( event->modifiers() == ( Qt::ShiftModifier | Qt::AltModifier ) ) && ( currentTool()->properties.width > -1 ) )
523  {
524  //adjust width if not locked
525  //currentTool()->startAdjusting( WIDTH, 0 );
526  //return;
527  }
528  if ( ( event->modifiers() == ( Qt::ControlModifier | Qt::AltModifier ) ) && ( currentTool()->properties.feather > -1 ) )
529  {
530  //adjust feather if not locked
531  currentTool()->startAdjusting( FEATHER, 0 );
532  return;
533  }
534  }
535 
536  // ---- checks layer availability ------
537  Layer* layer = mEditor->layers()->currentLayer();
538  Q_ASSUME( layer != nullptr );
539 
540  if ( layer->type() == Layer::VECTOR )
541  {
542  auto pLayerVector = static_cast< LayerVector* >( layer );
543  VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
544  Q_CHECK_PTR( vectorImage );
545  }
546  else if ( layer->type() == Layer::BITMAP )
547  {
548  auto pLayerBitmap = static_cast< LayerBitmap* >( layer );
549  BitmapImage* bitmapImage = pLayerBitmap->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 );
550  Q_CHECK_PTR( bitmapImage );
551  }
552 
553  if ( !layer->mVisible && currentTool()->type() != HAND && ( event->button() != Qt::RightButton ) )
554  {
555  QMessageBox::warning( this, tr( "Warning" ),
556  tr( "You are drawing on a hidden layer! Please select another layer (or make the current layer visible)." ),
557  QMessageBox::Ok,
558  QMessageBox::Ok );
559  mMouseInUse = false;
560  return;
561  }
562  // ---
563  mCurrentPixel = mStrokeManager->getCurrentPixel();
564  mCurrentPoint = mEditor->view()->mapScreenToCanvas( mCurrentPixel );
565  //qDebug() << "CurPoint: " << mCurrentPoint;
566 
567 
568  // the user is also pressing the mouse
569  if ( event->buttons() & Qt::LeftButton || event->buttons() & Qt::RightButton )
570  {
571  mOffset = mCurrentPoint - mLastPoint;
572  }
573 
574  if ( event->button() == Qt::RightButton )
575  {
576  mMouseRightButtonInUse = true;
577  getTool( HAND )->mousePressEvent( event );
578  return;
579  }
580 
581  currentTool()->mousePressEvent( event );
582 }
583 
584 void ScribbleArea::mouseMoveEvent( QMouseEvent *event )
585 {
586  if ( !areLayersSane() )
587  {
588  return;
589  }
590 
591  Q_EMIT refreshPreview();
592 
593  mStrokeManager->mouseMoveEvent( event );
594  mCurrentPixel = mStrokeManager->getCurrentPixel();
595  mCurrentPoint = mEditor->view()->mapScreenToCanvas( mCurrentPixel );
596 
597  // the user is also pressing the mouse (= dragging)
598  if ( event->buttons() & Qt::LeftButton || event->buttons() & Qt::RightButton )
599  {
600  mOffset = mCurrentPoint - mLastPoint;
601  // --- use SHIFT + drag to resize WIDTH / use CTRL + drag to resize FEATHER ---
602  if ( currentTool()->isAdjusting )
603  {
604  ToolPropertyType tool_type;
605  tool_type = event->modifiers() & Qt::ControlModifier ? FEATHER : WIDTH;
606  currentTool()->adjustCursor( mOffset.x(), tool_type ); //updates cursors given org width or feather and x
607  updateCanvasCursor();
608  return;
609  }
610  }
611 
612  if ( event->buttons() == Qt::RightButton )
613  {
614  getTool( HAND )->mouseMoveEvent( event );
615  return;
616  }
617 
618  currentTool()->mouseMoveEvent( event );
619 
620 #ifdef DEBUG_FPS
621  // debug fps count.
622  clock_t curTime = clock();
623  mDebugTimeQue.push_back( curTime );
624 
625  while ( mDebugTimeQue.size() > 100 )
626  {
627  mDebugTimeQue.pop_front();
628  }
629 
630  if ( mDebugTimeQue.size() > 2 )
631  {
632  clock_t interval = mDebugTimeQue.back() - mDebugTimeQue.front();
633  double fps = mDebugTimeQue.size() / ( ( double )interval ) * CLOCKS_PER_SEC;
634  //qDebug() << fps;
635  }
636 #endif
637 }
638 
639 void ScribbleArea::mouseReleaseEvent( QMouseEvent *event )
640 {
641  mMouseInUse = false;
642 
643  // ---- checks ------
644  if ( currentTool()->isAdjusting )
645  {
646  currentTool()->stopAdjusting();
647  mEditor->tools()->setWidth( currentTool()->properties.width );
648  return; // [SHIFT]+drag OR [CTRL]+drag
649  }
650 
651  mStrokeManager->mouseReleaseEvent( event );
652 
653  if ( event->button() == Qt::RightButton )
654  {
655  getTool( HAND )->mouseReleaseEvent( event );
656  mMouseRightButtonInUse = false;
657  return;
658  }
659 
660  currentTool()->mouseReleaseEvent( event );
661 
662  if ( currentTool()->type() == EYEDROPPER )
663  {
664  setCurrentTool( mPrevToolType );
665  }
666 
667  // ---- last check (at the very bottom of mouseRelease) ----
668  if ( instantTool && !mKeyboardInUse ) // temp tool and released all keys ?
669  {
670  setPrevTool();
671  }
672 }
673 
674 void ScribbleArea::mouseDoubleClickEvent( QMouseEvent *event )
675 {
676  currentTool()->mouseDoubleClickEvent( event );
677 }
678 
679 void ScribbleArea::resizeEvent( QResizeEvent *event )
680 {
681  QWidget::resizeEvent( event );
682  mCanvas = QPixmap( size() );
683  mCanvas.fill(Qt::transparent);
684 
685  this->setStyleSheet("background-color:yellow;");
686 
687  mEditor->view()->setCanvasSize( size() );
688  updateAllFrames();
689 }
690 
691 /************************************************************************************/
692 // paint methods
693 
694 void ScribbleArea::paintBitmapBuffer( )
695 {
696  Layer* layer = mEditor->layers()->currentLayer();
697 
698  // ---- checks ------
699  Q_ASSERT( layer );
700  if ( layer == NULL ) { return; } // TODO: remove in future.
701 
702 
703  // Clear the temporary pixel path
704  BitmapImage *targetImage = ( ( LayerBitmap * )layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 );
705  if ( targetImage != NULL )
706  {
707  QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver;
708  switch ( currentTool()->type() )
709  {
710  case ERASER:
711  cm = QPainter::CompositionMode_DestinationOut;
712  break;
713  case BRUSH:
714  case PEN:
715  case PENCIL:
716  if ( getTool( currentTool()->type() )->properties.preserveAlpha )
717  {
718  cm = QPainter::CompositionMode_SourceAtop;
719  }
720  break;
721  default: //nothing
722  break;
723  }
724  targetImage->paste( mBufferImg, cm );
725  }
726 
727  //qCDebug( mLog ) << "Paste Rect" << mBufferImg->bounds();
728 
729  QRect rect = mEditor->view()->getView().mapRect( mBufferImg->bounds() );
730 
731  // Clear the buffer
732  mBufferImg->clear();
733 
734  layer->setModified( mEditor->currentFrame(), true );
735  emit modification();
736 
737  int frameNumber = mEditor->currentFrame();
738  QPixmapCache::remove( mPixmapCacheKeys[frameNumber] );
739  mPixmapCacheKeys[frameNumber] = QPixmapCache::Key();
740 
741  drawCanvas( mEditor->currentFrame(), rect.adjusted( -1, -1, 1, 1 ) );
742  update( rect );
743 }
744 
745 void ScribbleArea::paintBitmapBufferRect( QRect rect )
746 {
747  if ( currentTool()->type() == SMUDGE || currentTool()->type() == BRUSH || mEditor->playback()->isPlaying() ) {
748  Layer* layer = mEditor->layers()->currentLayer();
749 
750  // ---- checks ------
751  Q_ASSERT( layer );
752  if ( layer == NULL ) { return; } // TODO: remove in future.
753 
754  BitmapImage *targetImage = ( ( LayerBitmap * )layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 );
755  // Clear the temporary pixel path
756  if ( targetImage != NULL )
757  {
758  QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver;
759  switch ( currentTool()->type() )
760  {
761  case ERASER:
762  cm = QPainter::CompositionMode_DestinationOut;
763  break;
764  case BRUSH:
765  case PEN:
766  case PENCIL:
767  if ( getTool( currentTool()->type() )->properties.preserveAlpha )
768  {
769  cm = QPainter::CompositionMode_SourceAtop;
770  }
771  break;
772  default: //nothing
773  break;
774  }
775  targetImage->paste( mBufferImg, cm );
776  }
777 
778  //qCDebug( mLog ) << "Paste Rect" << mBufferImg->bounds();
779 
780  // Clear the buffer
781  mBufferImg->clear();
782 
783  layer->setModified( mEditor->currentFrame(), true );
784  emit modification();
785 
786  int frameNumber = mEditor->currentFrame();
787  QPixmapCache::remove( mPixmapCacheKeys[frameNumber] );
788  mPixmapCacheKeys[frameNumber] = QPixmapCache::Key();
789 
790  drawCanvas( mEditor->currentFrame(), rect.adjusted( -1, -1, 1, 1 ) );
791  update( rect );
792  }
793 }
794 
795 void ScribbleArea::clearBitmapBuffer()
796 {
797  mBufferImg->clear();
798 }
799 
800 void ScribbleArea::drawLine( QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm )
801 {
802  mBufferImg->drawLine( P1, P2, pen, cm, mPrefs->isOn( SETTING::ANTIALIAS ) );
803 }
804 
805 void ScribbleArea::drawPath( QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm )
806 {
807  mBufferImg->drawPath( path, pen, brush, cm, mPrefs->isOn( SETTING::ANTIALIAS ) );
808 }
809 
810 void ScribbleArea::refreshBitmap( const QRectF& rect, int rad )
811 {
812  QRectF updatedRect = mEditor->view()->mapCanvasToScreen( rect.normalized().adjusted( -rad, -rad, +rad, +rad ) );
813  update( updatedRect.toRect() );
814 }
815 
816 void ScribbleArea::refreshVector( const QRectF& rect, int rad )
817 {
818  rad += 1;
819  //QRectF updatedRect = mEditor->view()->mapCanvasToScreen( rect.normalized().adjusted( -rad, -rad, +rad, +rad ) );
820  update( rect.normalized().adjusted( -rad, -rad, +rad, +rad ).toRect() );
821 
822  //qDebug() << "Logical: " << rect;
823  //qDebug() << "Physical: " << mEditor->view()->mapCanvasToScreen( rect.normalized() );
824  //update();
825 }
826 
827 void ScribbleArea::paintCanvasCursor( QPainter& painter )
828 {
829  Layer* layer = mEditor->layers()->currentLayer();
830  QPoint center(0, 0);
831  QTransform view = mEditor->view()->getView();
832  QPoint mousePos = currentTool()->getCurrentPoint().toPoint();
833  QPoint transformedPos = view.map( mousePos );
834  QPixmap transCursorImg = mCursorImg.transformed(view);
835  int centerCal = mCursorImg.width() / 2;
836  center.setX( centerCal );
837  center.setY( centerCal );
838  if ( layer->type() == Layer::VECTOR )
839  {
840  painter.setTransform( view );
841  }
842  painter.drawPixmap( QPoint( mousePos.x() - center.x(),
843  mousePos.y() - center.y() ),
844  mCursorImg );
845 
846  // update center of transformed img for rect only
847  centerCal = transCursorImg.width() / 2;
848  center.setX( centerCal );
849  center.setY( centerCal );
850 
851  // clear and update cursor rect
852  update( transCursorImg.rect().adjusted( -3, -3, 3, 3 )
853  .translated( transformedPos.x() - center.x(),
854  transformedPos.y() - center.y() ) );
855 }
856 
857 void ScribbleArea::updateCanvasCursor()
858 {
859  if ( currentTool()->isAdjusting )
860  {
861  mCursorImg = currentTool()->quickSizeCursor();
862  }
863  else if ( mEditor->preference()->isOn( SETTING::DOTTED_CURSOR ) )
864  {
865  mCursorImg = currentTool()->canvasCursor();
866  } else
867  {
868  // if above does not comply, delocate image
869  mCursorImg = QPixmap();
870  }
871 }
872 
873 void ScribbleArea::paintEvent( QPaintEvent* event )
874 {
875  if ( !mMouseInUse || currentTool()->type() == MOVE || currentTool()->type() == HAND || mMouseRightButtonInUse)
876  {
877  // --- we retrieve the canvas from the cache; we create it if it doesn't exist
878  int curIndex = mEditor->currentFrame();
879  int frameNumber = mEditor->layers()->LastFrameAtFrame( curIndex );
880 
881  QPixmapCache::Key cachedKey = mPixmapCacheKeys[frameNumber];
882 
883  if ( !QPixmapCache::find( cachedKey, &mCanvas ) )
884  {
885  drawCanvas( mEditor->currentFrame(), event->rect() );
886 
887  mPixmapCacheKeys[frameNumber] = QPixmapCache::insert( mCanvas );
888  //qDebug() << "Repaint canvas!";
889  }
890  }
891 
892  if ( currentTool()->type() == MOVE )
893  {
894  Layer* layer = mEditor->layers()->currentLayer();
895  Q_CHECK_PTR( layer );
896  if ( layer->type() == Layer::VECTOR )
897  {
898  auto vecLayer = static_cast< LayerVector* >( layer );
899  vecLayer->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 )->setModified( true );
900  }
901  }
902 
903  QPainter painter( this );
904 
905  // paints the canvas
906  painter.setWorldMatrixEnabled( false );
907  //painter.setTransform( transMatrix ); // FIXME: drag canvas by hand
908 
909  painter.drawPixmap( QPoint( 0, 0 ), mCanvas );
910 
911  Layer* layer = mEditor->layers()->currentLayer();
912 
913  if ( !editor()->playback()->isPlaying() ) // we don't need to display the following when the animation is playing
914  {
915  if ( layer->type() == Layer::VECTOR )
916  {
917  VectorImage *vectorImage = ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
918 
919  switch ( currentTool()->type() )
920  {
921  case SMUDGE:
922  case HAND:
923  {
924  painter.save();
925  painter.setWorldMatrixEnabled( false );
926  painter.setRenderHint( QPainter::Antialiasing, false );
927  // ----- paints the edited elements
928  QPen pen2( Qt::black, 0.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin );
929  painter.setPen( pen2 );
930  QColor colour;
931  // ------------ vertices of the edited curves
932  colour = QColor( 200, 200, 200 );
933  painter.setBrush( colour );
934  for ( int k = 0; k < vectorSelection.curve.size(); k++ )
935  {
936  int curveNumber = vectorSelection.curve.at( k );
937 
938  for ( int vertexNumber = -1; vertexNumber < vectorImage->getCurveSize( curveNumber ); vertexNumber++ )
939  {
940  QPointF vertexPoint = vectorImage->getVertex( curveNumber, vertexNumber );
941  QRectF rectangle( mEditor->view()->mapCanvasToScreen( vertexPoint ) - QPointF( 3.0, 3.0 ), QSizeF( 7, 7 ) );
942  if ( rect().contains( mEditor->view()->mapCanvasToScreen( vertexPoint ).toPoint() ) )
943  {
944  painter.drawRect( rectangle );
945  }
946  }
947  }
948  // ------------ selected vertices of the edited curves
949  colour = QColor( 100, 100, 255 );
950  painter.setBrush( colour );
951  for ( int k = 0; k < vectorSelection.vertex.size(); k++ )
952  {
953  VertexRef vertexRef = vectorSelection.vertex.at( k );
954  QPointF vertexPoint = vectorImage->getVertex( vertexRef );
955  QRectF rectangle0 = QRectF( mEditor->view()->mapCanvasToScreen( vertexPoint ) - QPointF( 3.0, 3.0 ), QSizeF( 7, 7 ) );
956  painter.drawRect( rectangle0 );
957  }
958  // ----- paints the closest vertices
959  colour = QColor( 255, 0, 0 );
960  painter.setBrush( colour );
961  if ( vectorSelection.curve.size() > 0 )
962  {
963  for ( int k = 0; k < mClosestVertices.size(); k++ )
964  {
965  VertexRef vertexRef = mClosestVertices.at( k );
966  QPointF vertexPoint = vectorImage->getVertex( vertexRef );
967 
968  QRectF rectangle = QRectF( mEditor->view()->mapCanvasToScreen( vertexPoint ) - QPointF( 3.0, 3.0 ), QSizeF( 7, 7 ) );
969  painter.drawRect( rectangle );
970  }
971  }
972  painter.restore();
973  break;
974  }
975 
976  case MOVE:
977  {
978  // ----- paints the closest curves
979  mBufferImg->clear();
980  QColor colour = QColor( 100, 100, 255, 50 );
981  QPen pen2( colour, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin );
982 
983 
984  for ( int k = 0; k < mClosestCurves.size(); k++ )
985  {
986  float scale = mEditor->view()->scaling(); // FIXME: check whether it's correct (det = area?)
987 
988  int idx = mClosestCurves[ k ];
989  if ( vectorImage->m_curves.size() <= idx )
990  {
991  // safety check
992  continue;
993  }
994  BezierCurve myCurve = vectorImage->m_curves[ mClosestCurves[ k ] ];
995  if ( myCurve.isPartlySelected() )
996  {
997  myCurve.transform( selectionTransformation );
998  }
999  QPainterPath path = myCurve.getStrokedPath( 1.2 / scale, false );
1000  mBufferImg->drawPath( mEditor->view()->mapCanvasToScreen( path ),
1001  pen2,
1002  colour,
1003  QPainter::CompositionMode_SourceOver,
1004  mPrefs->isOn( SETTING::ANTIALIAS ) );
1005  }
1006  break;
1007  }
1008 
1009  default:
1010  {
1011  break;
1012  }
1013  } // end siwtch
1014  }
1015 
1016  // paints the buffer image
1017  if ( mEditor->layers()->currentLayer() != NULL )
1018  {
1019  painter.setOpacity( 1.0 );
1020  if ( mEditor->layers()->currentLayer()->type() == Layer::BITMAP )
1021  {
1022  painter.setWorldMatrixEnabled( true );
1023  painter.setTransform( mEditor->view()->getView() );
1024  }
1025  else if ( mEditor->layers()->currentLayer()->type() == Layer::VECTOR )
1026  {
1027  painter.setWorldMatrixEnabled( false );
1028  }
1029 
1030  // TODO: move to above if vector statement
1031  mBufferImg->paintImage( painter );
1032 
1033  paintCanvasCursor( painter );
1034  }
1035 
1036  mCanvasRenderer.renderGrid(painter);
1037 
1038  // paints the selection outline
1039  if ( somethingSelected && ( myTempTransformedSelection.isValid() || mMoveMode == ROTATION ) ) // @revise
1040  {
1041  // outline of the transformed selection
1042  painter.setWorldMatrixEnabled( false );
1043  painter.setOpacity( 1.0 );
1044  QPolygon tempRect = mEditor->view()->getView().mapToPolygon( myTempTransformedSelection.normalized().toRect() );
1045 
1046  Layer* layer = mEditor->layers()->currentLayer();
1047  if ( layer != NULL )
1048  {
1049  if ( layer->type() == Layer::BITMAP )
1050  {
1051  painter.setBrush( Qt::NoBrush );
1052  painter.setPen( Qt::DashLine );
1053  }
1054  if ( layer->type() == Layer::VECTOR )
1055  {
1056  painter.setBrush( QColor( 0, 0, 0, 20 ) );
1057  painter.setPen( Qt::gray );
1058  }
1059  painter.drawPolygon( tempRect );
1060 
1061  if ( layer->type() != Layer::VECTOR || currentTool()->type() != SELECT )
1062  {
1063  painter.setPen( Qt::SolidLine );
1064  painter.setBrush( QBrush( Qt::gray ) );
1065  painter.drawRect( tempRect.point( 0 ).x() - 3, tempRect.point( 0 ).y() - 3, 6, 6 );
1066  painter.drawRect( tempRect.point( 1 ).x() - 3, tempRect.point( 1 ).y() - 3, 6, 6 );
1067  painter.drawRect( tempRect.point( 2 ).x() - 3, tempRect.point( 2 ).y() - 3, 6, 6 );
1068  painter.drawRect( tempRect.point( 3 ).x() - 3, tempRect.point( 3 ).y() - 3, 6, 6 );
1069  }
1070  }
1071  }
1072  }
1073 
1074  // outlines the frame of the viewport
1075 #ifdef _DEBUG
1076  painter.setWorldMatrixEnabled( false );
1077  painter.setPen( QPen( Qt::gray, 2 ) );
1078  painter.setBrush( Qt::NoBrush );
1079  painter.drawRect( QRect( 0, 0, width(), height() ) );
1080 #endif
1081 
1082  event->accept();
1083 }
1084 
1085 void ScribbleArea::drawCanvas( int frame, QRect rect )
1086 {
1087  Object* object = mEditor->object();
1088 
1089  RenderOptions options;
1090  options.bPrevOnionSkin = mPrefs->isOn( SETTING::PREV_ONION );
1091  options.bNextOnionSkin = mPrefs->isOn( SETTING::NEXT_ONION );
1092  options.bColorizePrevOnion = mPrefs->isOn(SETTING::ONION_RED);
1093  options.bColorizeNextOnion = mPrefs->isOn(SETTING::ONION_BLUE);
1094  options.nPrevOnionSkinCount = mPrefs->getInt(SETTING::ONION_PREV_FRAMES_NUM);
1095  options.nNextOnionSkinCount = mPrefs->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
1096  options.fOnionSkinMaxOpacity = mPrefs->getInt(SETTING::ONION_MAX_OPACITY);
1097  options.fOnionSkinMinOpacity = mPrefs->getInt(SETTING::ONION_MIN_OPACITY);
1098  options.bAntiAlias = mPrefs->isOn( SETTING::ANTIALIAS );
1099  options.bGrid = mPrefs->isOn( SETTING::GRID );
1100  options.nGridSize = mPrefs->getInt( SETTING::GRID_SIZE );
1101  options.bAxis = mPrefs->isOn( SETTING::AXIS );
1102  options.bThinLines = mPrefs->isOn( SETTING::INVISIBLE_LINES );
1103  options.bOutlines = mPrefs->isOn( SETTING::OUTLINES );
1104  options.nShowAllLayers = mShowAllLayers;
1105  options.bIsOnionAbsolute = (mPrefs->getString( SETTING::ONION_TYPE ) == "absolute");
1106 
1107  mCanvasRenderer.setOptions( options );
1108 
1109  //qDebug() << "Antialias=" << options.bAntiAlias;
1110 
1111  mCanvasRenderer.setCanvas( &mCanvas );
1112  mCanvasRenderer.setViewTransform( mEditor->view()->getView() );
1113  mCanvasRenderer.paint( object, mEditor->layers()->currentLayerIndex(), frame, rect );
1114 
1115  return;
1116 }
1117 
1118 void ScribbleArea::setGaussianGradient( QGradient &gradient, QColor colour, qreal opacity, qreal mOffset )
1119 {
1120  if (mOffset < 0) {
1121  mOffset = 0;
1122  }
1123  if (mOffset > 100) {
1124  mOffset = 100;
1125  }
1126  int r = colour.red();
1127  int g = colour.green();
1128  int b = colour.blue();
1129  qreal a = colour.alphaF();
1130 
1131  int mainColorAlpha = qRound( a * 255 * opacity );
1132 
1133  // the more feather (offset), the more softness (opacity)
1134  //
1135  int alphaAdded = qRound((mainColorAlpha * mOffset) / 100);
1136 
1137  gradient.setColorAt( 0.0, QColor( r, g, b, mainColorAlpha - alphaAdded ) );
1138  gradient.setColorAt( 1.0, QColor( r, g, b, 0 ) );
1139  gradient.setColorAt( 1.0 - (mOffset/100.0), QColor( r, g, b, mainColorAlpha - alphaAdded ) );
1140 }
1141 
1142 void ScribbleArea::drawPen( QPointF thePoint, qreal brushWidth, QColor fillColour, bool useAA )
1143 {
1144  QRectF rectangle( thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth );
1145 
1146  mBufferImg->drawEllipse( rectangle, Qt::NoPen, QBrush(fillColour, Qt::SolidPattern),
1147  QPainter::CompositionMode_Source, useAA );
1148 }
1149 
1150 void ScribbleArea::drawPencil( QPointF thePoint, qreal brushWidth, QColor fillColour, qreal opacity )
1151 {
1152  drawBrush(thePoint, brushWidth, 50, fillColour, opacity, true);
1153 }
1154 
1155 void ScribbleArea::drawBrush( QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColour, qreal opacity, bool usingFeather, int useAA )
1156 {
1157  QRectF rectangle( thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth );
1158 
1159  BitmapImage gradientImg;
1160  if (usingFeather==true)
1161  {
1162  QRadialGradient radialGrad( thePoint, 0.5 * brushWidth );
1163  setGaussianGradient( radialGrad, fillColour, opacity, mOffset );
1164 
1165  gradientImg.drawEllipse( rectangle, Qt::NoPen, radialGrad,
1166  QPainter::CompositionMode_SourceOver, false );
1167  }
1168  else
1169  {
1170  mBufferImg->drawEllipse( rectangle, Qt::NoPen, QBrush(fillColour, Qt::SolidPattern),
1171  QPainter::CompositionMode_SourceOver, useAA );
1172  }
1173  mBufferImg->paste( &gradientImg );
1174 }
1175 
1180 void ScribbleArea::flipSelection(bool flipVertical)
1181 {
1182  int scaleX = myTempTransformedSelection.width() / mySelection.width();
1183  int scaleY = myTempTransformedSelection.height() / mySelection.height();
1184  QVector<QPoint> centerPoints = calcSelectionCenterPoints();
1185 
1186  QTransform translate = QTransform::fromTranslate( centerPoints[0].x(), centerPoints[0].y() );
1187  QTransform _translate = QTransform::fromTranslate( -centerPoints[1].x(), -centerPoints[1].y() );
1188  QTransform scale = QTransform::fromScale( -scaleX, scaleY );
1189 
1190  if (flipVertical == true)
1191  {
1192  scale = QTransform::fromScale( scaleX, -scaleY );
1193  }
1194 
1195  // reset transformation for vector selections
1196  selectionTransformation.reset();
1197  selectionTransformation *= _translate *
1198  scale *
1199  translate;
1200 
1201  paintTransformedSelection();
1202  applyTransformedSelection();
1203 }
1204 
1205 void ScribbleArea::blurBrush( BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_ )
1206 {
1207  QRadialGradient radialGrad( thePoint_, 0.5 * brushWidth_ );
1208  setGaussianGradient( radialGrad, QColor( 255, 255, 255, 127 ), opacity_, mOffset_ );
1209 
1210  QRectF srcRect( srcPoint_.x() - 0.5 * brushWidth_, srcPoint_.y() - 0.5 * brushWidth_, brushWidth_, brushWidth_ );
1211  QRectF trgRect( thePoint_.x() - 0.5 * brushWidth_, thePoint_.y() - 0.5 * brushWidth_, brushWidth_, brushWidth_ );
1212 
1213  BitmapImage bmiSrcClip = bmiSource_->copy( srcRect.toRect() );
1214  BitmapImage bmiTmpClip = bmiSrcClip; // todo: find a shorter way
1215 
1216  bmiTmpClip.drawRect( srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn( SETTING::ANTIALIAS ) );
1217  bmiSrcClip.bounds().moveTo( trgRect.topLeft().toPoint() );
1218  bmiTmpClip.paste( &bmiSrcClip, QPainter::CompositionMode_SourceAtop );
1219  mBufferImg->paste( &bmiTmpClip );
1220 }
1221 
1222 void ScribbleArea::liquifyBrush( BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_ )
1223 {
1224  QPointF delta = ( thePoint_ - srcPoint_ ); // increment vector
1225  QRectF trgRect( thePoint_.x() - 0.5 * brushWidth_, thePoint_.y() - 0.5 * brushWidth_, brushWidth_, brushWidth_ );
1226 
1227  QRadialGradient radialGrad( thePoint_, 0.5 * brushWidth_ );
1228  setGaussianGradient( radialGrad, QColor( 255, 255, 255, 255 ), opacity_, mOffset_ );
1229 
1230  // Create gradient brush
1231  BitmapImage* bmiTmpClip = new BitmapImage;
1232  bmiTmpClip->drawRect( trgRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn( SETTING::ANTIALIAS ) );
1233 
1234  // Slide texture/pixels of the source image
1235  qreal factor, factorGrad;
1236  int xb, yb, xa, ya;
1237 
1238  for ( yb = bmiTmpClip->bounds().top(); yb < bmiTmpClip->bounds().bottom(); yb++ )
1239  {
1240  for ( xb = bmiTmpClip->bounds().left(); xb < bmiTmpClip->bounds().right(); xb++ )
1241  {
1242  QColor color;
1243  color.setRgba( bmiTmpClip->pixel( xb, yb ) );
1244  factorGrad = color.alphaF(); // any from r g b a is ok
1245 
1246  xa = xb - factorGrad*delta.x();
1247  ya = yb - factorGrad*delta.y();
1248 
1249  color.setRgba( bmiSource_->pixel( xa, ya ) );
1250  factor = color.alphaF();
1251 
1252  if ( factor > 0.0 )
1253  {
1254  color.setRed( color.red() / factor );
1255  color.setGreen( color.green() / factor );
1256  color.setBlue( color.blue() / factor );
1257  color.setAlpha( 255 ); // Premultiplied color
1258 
1259  color.setRed( color.red()*factorGrad );
1260  color.setGreen( color.green()*factorGrad );
1261  color.setBlue( color.blue()*factorGrad );
1262  color.setAlpha( 255 * factorGrad ); // Premultiplied color
1263 
1264  bmiTmpClip->setPixel( xb, yb, color.rgba() );
1265  }
1266  else {
1267  bmiTmpClip->setPixel( xb, yb, qRgba( 255, 255, 255, 255 ) );
1268  }
1269  }
1270  }
1271  mBufferImg->paste( bmiTmpClip );
1272  delete bmiTmpClip;
1273 }
1274 
1275 void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA)
1276 {
1277  QRectF updateRect = mEditor->view()->mapCanvasToScreen( path.boundingRect().toRect() ).adjusted( -1, -1, 1, 1);
1278 
1279  // Update region outside updateRect
1280  QRectF boundingRect = updateRect.adjusted(-width(),-height(), width(),height());
1281  mBufferImg->clear();
1282  mBufferImg->drawPath( path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA);
1283  update( boundingRect.toRect());
1284 
1285 }
1286 
1287 /************************************************************************************/
1288 // view handling
1289 
1290 QTransform ScribbleArea::getView()
1291 {
1292  Layer* layer = mEditor->layers()->currentLayer();
1293  if ( layer == NULL )
1294  {
1295  Q_ASSERT( false );
1296  return QTransform(); // TODO: error
1297  }
1298 
1299  if ( layer->type() == Layer::CAMERA )
1300  {
1301  return ( ( LayerCamera * )layer )->getViewAtFrame( mEditor->currentFrame() );
1302  }
1303  else
1304  {
1305  return mEditor->view()->getView();
1306  }
1307 }
1308 
1309 QRectF ScribbleArea::getViewRect()
1310 {
1311  QRectF rect = QRectF( -width() / 2, -height() / 2, width(), height() );
1312  Layer* layer = mEditor->layers()->currentLayer();
1313  if ( layer == NULL ) { return rect; }
1314  if ( layer->type() == Layer::CAMERA )
1315  {
1316  return ( ( LayerCamera * )layer )->getViewRect();
1317  }
1318  else
1319  {
1320  return rect;
1321  }
1322 }
1323 
1324 QRectF ScribbleArea::getCameraRect()
1325 {
1326  return mCanvasRenderer.getCameraRect();
1327 }
1328 
1329 QPointF ScribbleArea::getCentralPoint()
1330 {
1331  return mEditor->view()->mapScreenToCanvas( QPointF( width() / 2, height() / 2 ) );
1332 }
1333 
1334 /************************************************************************************/
1335 // selection handling
1336 
1337 void ScribbleArea::calculateSelectionRect()
1338 {
1339  selectionTransformation.reset();
1340  Layer* layer = mEditor->layers()->currentLayer();
1341  if ( layer == NULL ) { return; }
1342  if ( layer->type() == Layer::VECTOR )
1343  {
1344  VectorImage *vectorImage = ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
1345  vectorImage->calculateSelectionRect();
1346  setSelection( vectorImage->getSelectionRect(), true );
1347  }
1348 }
1349 
1355 {
1356  QVector<QPoint> centerPoints;
1357  float selectionCenterX,
1358  selectionCenterY,
1359  tempSelectionCenterX,
1360  tempSelectionCenterY;
1361 
1362  tempSelectionCenterX = 0.5 * ( myTempTransformedSelection.left() +
1363  myTempTransformedSelection.right() );
1364  tempSelectionCenterY = 0.5 * ( myTempTransformedSelection.top() +
1365  myTempTransformedSelection.bottom() );
1366  selectionCenterX = 0.5 * ( mySelection.left() + mySelection.right() );
1367  selectionCenterY = 0.5 * ( mySelection.top() + mySelection.bottom() );
1368  centerPoints.append( QPoint( tempSelectionCenterX, tempSelectionCenterY ) );
1369  centerPoints.append( QPoint( selectionCenterX, selectionCenterY ) );
1370  return centerPoints;
1371 }
1372 
1373 void ScribbleArea::calculateSelectionTransformation() // Vector layer transform
1374 {
1375  float scaleX, scaleY;
1376  QVector<QPoint> centerPoints = calcSelectionCenterPoints();
1377 
1378  if ( mySelection.width() == 0 )
1379  {
1380  scaleX = 1.0;
1381  }
1382  else
1383  {
1384  scaleX = myTempTransformedSelection.width() / mySelection.width();
1385  }
1386 
1387  if ( mySelection.height() == 0 )
1388  {
1389  scaleY = 1.0;
1390  }
1391  else
1392  {
1393  scaleY = myTempTransformedSelection.height() / mySelection.height();
1394  }
1395 
1396  selectionTransformation.reset();
1397 
1398  selectionTransformation.translate( centerPoints[0].x(), centerPoints[0].y() );
1399  selectionTransformation.rotate(myRotatedAngle);
1400  selectionTransformation.scale( scaleX, scaleY );
1401  selectionTransformation.translate( -centerPoints[1].x(), -centerPoints[1].y() );
1402 
1403 // modification();
1404 }
1405 
1406 
1407 void ScribbleArea::paintTransformedSelection()
1408 {
1409  Layer* layer = mEditor->layers()->currentLayer();
1410  if ( layer == NULL )
1411  {
1412  return;
1413  }
1414 
1415  if ( somethingSelected ) // there is something selected
1416  {
1417  if ( layer->type() == Layer::BITMAP )
1418  {
1419  mCanvasRenderer.setTransformedSelection(mySelection.toRect(), selectionTransformation);
1420  }
1421  else if ( layer->type() == Layer::VECTOR )
1422  {
1423  // vector transformation
1424  LayerVector *layerVector = ( LayerVector * )layer;
1425  VectorImage *vectorImage = layerVector->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
1426  vectorImage->setSelectionTransformation( selectionTransformation );
1427 
1428  }
1429  setModified( mEditor->layers()->currentLayerIndex(), mEditor->currentFrame() );
1430  }
1431  update();
1432 }
1433 
1434 void ScribbleArea::applyTransformedSelection()
1435 {
1436  mCanvasRenderer.ignoreTransformedSelection();
1437 
1438  Layer* layer = mEditor->layers()->currentLayer();
1439  if ( layer == NULL )
1440  {
1441  return;
1442  }
1443 
1444  if ( somethingSelected ) // there is something selected
1445  {
1446  if ( layer->type() == Layer::BITMAP )
1447  {
1448  BitmapImage* bitmapImage = dynamic_cast< LayerBitmap* >( layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 );
1449 
1450  BitmapImage* transformedImage = new BitmapImage(bitmapImage->transformed(mySelection.toRect(), selectionTransformation, mPrefs->isOn(SETTING::ANTIALIAS)));
1451 
1452  bitmapImage->clear(mySelection);
1453  bitmapImage->paste(transformedImage, QPainter::CompositionMode_SourceOver);
1454 
1455  delete transformedImage;
1456  }
1457  else if ( layer->type() == Layer::VECTOR )
1458  {
1459 
1460  VectorImage *vectorImage = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
1461  vectorImage->applySelectionTransformation();
1462 
1463  }
1464 
1465  setModified( mEditor->layers()->currentLayerIndex(), mEditor->currentFrame() );
1466  }
1467 
1468  updateCurrentFrame();
1469 }
1470 
1471 void ScribbleArea::cancelTransformedSelection()
1472 {
1473  mCanvasRenderer.ignoreTransformedSelection();
1474 
1475  if (somethingSelected) {
1476 
1477  Layer* layer = mEditor->layers()->currentLayer();
1478  if ( layer == NULL )
1479  {
1480  return;
1481  }
1482 
1483  if ( layer->type() == Layer::VECTOR ) {
1484 
1485  VectorImage *vectorImage = ((LayerVector *)layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
1486  vectorImage->setSelectionTransformation( QTransform() );
1487  }
1488 
1489  setSelection( mySelection, true );
1490 
1491  selectionTransformation.reset();
1492 
1493  mOffset.setX( 0 );
1494  mOffset.setY( 0 );
1495 
1496  updateCurrentFrame();
1497  }
1498 }
1499 
1500 void ScribbleArea::setSelection( QRectF rect, bool trueOrFalse )
1501 {
1502 
1503  mySelection = rect;
1504  myTransformedSelection = rect;
1505  myTempTransformedSelection = rect;
1506  somethingSelected = trueOrFalse;
1507 
1508 
1509  // Temporary disabled this as it breaks selection rotate key (ctrl) event.
1510  //
1511  // displaySelectionProperties();
1512 }
1513 
1514 void ScribbleArea::displaySelectionProperties()
1515 {
1516  Layer* layer = mEditor->layers()->currentLayer();
1517  if ( layer == NULL ) { return; }
1518  if ( layer->type() == Layer::VECTOR )
1519  {
1520  LayerVector *layerVector = ( LayerVector * )layer;
1521  VectorImage *vectorImage = layerVector->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
1522  //vectorImage->applySelectionTransformation();
1523  if ( currentTool()->type() == MOVE )
1524  {
1525  int selectedCurve = vectorImage->getFirstSelectedCurve();
1526  if ( selectedCurve != -1 )
1527  {
1528  mEditor->tools()->setWidth( vectorImage->m_curves[ selectedCurve ].getWidth() );
1529  mEditor->tools()->setFeather( vectorImage->m_curves[ selectedCurve ].getFeather() );
1530  mEditor->tools()->setInvisibility( vectorImage->m_curves[ selectedCurve ].isInvisible() );
1531  mEditor->tools()->setPressure( vectorImage->m_curves[ selectedCurve ].getVariableWidth() );
1532  mEditor->color()->setColorNumber( vectorImage->m_curves[ selectedCurve ].getColourNumber() );
1533  }
1534 
1535  int selectedArea = vectorImage->getFirstSelectedArea();
1536  if ( selectedArea != -1 )
1537  {
1538  mEditor->color()->setColorNumber( vectorImage->area[ selectedArea ].mColourNumber );
1539  }
1540  }
1541  }
1542 }
1543 
1544 void ScribbleArea::selectAll()
1545 {
1546  mOffset.setX( 0 );
1547  mOffset.setY( 0 );
1548  Layer* layer = mEditor->layers()->currentLayer();
1549 
1550  Q_ASSERT( layer );
1551  if ( layer == NULL ) { return; }
1552 
1553  if ( layer->type() == Layer::BITMAP )
1554  {
1555  // Only selects the entire screen erea
1556  //setSelection( mEditor->view()->mapScreenToCanvas( QRectF( -2, -2, width() + 3, height() + 3 ) ), true ); // TO BE IMPROVED
1557 
1558  // Selects the drawn area (bigger or smaller than the screen). It may be more accurate to select all this way
1559  // as the drawing area is not limited
1560  //
1561  BitmapImage *bitmapImage = ( ( LayerBitmap * )layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 );
1562  setSelection(bitmapImage->bounds(), true);
1563 
1564 
1565  }
1566  else if ( layer->type() == Layer::VECTOR )
1567  {
1568  VectorImage *vectorImage = ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
1569  vectorImage->selectAll();
1570  setSelection( vectorImage->getSelectionRect(), true );
1571  }
1572  updateCurrentFrame();
1573 }
1574 
1581 {
1582  mOffset = QPoint(0,0);
1583  myRotatedAngle = 0;
1584  selectionTransformation.reset();
1585 }
1586 
1587 void ScribbleArea::deselectAll()
1588 {
1590  mySelection.setRect( 10, 10, 20, 20 );
1591  myTransformedSelection.setRect( 10, 10, 20, 20 );
1592  myTempTransformedSelection.setRect( 10, 10, 20, 20 );
1593 
1594  Layer* layer = mEditor->layers()->currentLayer();
1595  if ( layer == NULL ) { return; }
1596  if ( layer->type() == Layer::VECTOR )
1597  {
1598  ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 )->deselectAll();
1599  }
1600  somethingSelected = false;
1601  isTransforming = false;
1602 
1603  mBufferImg->clear();
1604 
1605  //mBitmapSelection.clear();
1606  vectorSelection.clear();
1607 
1608  // clear all the data tools may have accumulated
1609  editor()->tools()->cleanupAllToolsData();
1610 
1611  updateCurrentFrame();
1612 }
1613 
1614 void ScribbleArea::toggleThinLines()
1615 {
1616  bool previousValue = mPrefs->isOn(SETTING::INVISIBLE_LINES);
1617  setEffect( SETTING::INVISIBLE_LINES, !previousValue );
1618 }
1619 
1620 void ScribbleArea::toggleOutlines()
1621 {
1622  mIsSimplified = !mIsSimplified;
1623  setEffect( SETTING::OUTLINES, mIsSimplified );
1624 }
1625 
1626 void ScribbleArea::toggleShowAllLayers()
1627 {
1628  mShowAllLayers++;
1629  if ( mShowAllLayers == 3 )
1630  {
1631  mShowAllLayers = 0;
1632  }
1633  updateAllFrames();
1634 }
1635 
1636 /************************************************************************************/
1637 // tool handling
1638 
1639 BaseTool* ScribbleArea::currentTool()
1640 {
1641  return editor()->tools()->currentTool();
1642 }
1643 
1644 BaseTool* ScribbleArea::getTool( ToolType eToolType )
1645 {
1646  return editor()->tools()->getTool( eToolType );
1647 }
1648 
1649 // TODO: check this method
1650 void ScribbleArea::setCurrentTool( ToolType eToolMode )
1651 {
1652  if ( currentTool() != NULL && eToolMode != currentTool()->type() )
1653  {
1654  qDebug() << "Set Current Tool" << BaseTool::TypeName( eToolMode );
1655  if ( BaseTool::TypeName( eToolMode ) == "" )
1656  {
1657  // tool does not exist
1658  //Q_ASSERT_X( false, "", "" );
1659  return;
1660  }
1661 
1662  if ( currentTool()->type() == MOVE )
1663  {
1664  paintTransformedSelection();
1665  deselectAll();
1666  }
1667  else if ( currentTool()->type() == POLYLINE )
1668  {
1669  deselectAll();
1670  }
1671  }
1672 
1673  mPrevToolType = currentTool()->type();
1674 
1675  // --- change cursor ---
1676  setCursor( currentTool()->cursor() );
1677  updateCanvasCursor();
1678  qDebug() << "fn: setCurrentTool " << "call: setCursor()" << "current tool" << currentTool()->typeName();
1679 }
1680 
1681 void ScribbleArea::setTemporaryTool( ToolType eToolMode )
1682 {
1683  // Only switch to remporary tool if not already in this state
1684  // and temporary tool is not already the current tool.
1685  //
1686  if (!instantTool && currentTool()->type() != eToolMode) {
1687 
1688  instantTool = true; // used to return to previous tool when finished (keyRelease).
1689  mPrevTemporalToolType = currentTool()->type();
1690  editor()->tools()->setCurrentTool( eToolMode );
1691  }
1692 }
1693 
1694 void ScribbleArea::deleteSelection()
1695 {
1696  if ( somethingSelected ) // there is something selected
1697  {
1698  Layer* layer = mEditor->layers()->currentLayer();
1699  if ( layer == NULL ) { return; }
1700  mEditor->backup( tr( "Delete Selection" ) );
1701  mClosestCurves.clear();
1702  if ( layer->type() == Layer::VECTOR ) { ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 )->deleteSelection(); }
1703  if ( layer->type() == Layer::BITMAP ) { ( ( LayerBitmap * )layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 )->clear( mySelection ); }
1704  updateAllFrames();
1705  }
1706 }
1707 
1708 void ScribbleArea::clearImage()
1709 {
1710  Layer* layer = mEditor->layers()->currentLayer();
1711  if ( layer == NULL ) { return; }
1712  if ( layer->type() == Layer::VECTOR )
1713  {
1714  mEditor->backup( tr( "Clear Image" ) ); // undo: only before change (just before)
1715  ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 )->clear();
1716  mClosestCurves.clear();
1717  mClosestVertices.clear();
1718  }
1719  else if ( layer->type() == Layer::BITMAP )
1720  {
1721  mEditor->backup( tr( "Clear Image" ) );
1722  ( ( LayerBitmap * )layer )->getLastBitmapImageAtFrame( mEditor->currentFrame(), 0 )->clear();
1723  }
1724  else
1725  {
1726  return; // skip updates when nothing changes
1727  }
1728 
1729  setModified( mEditor->layers()->currentLayerIndex(), mEditor->currentFrame() );
1730 }
1731 
1732 void ScribbleArea::setPrevTool()
1733 {
1734  editor()->tools()->setCurrentTool( mPrevTemporalToolType );
1735  instantTool = false;
1736 }
1737 
1738 /* Render Canvas */
1739 
1740 void ScribbleArea::paletteColorChanged(QColor color)
1741 {
1742  Q_UNUSED(color);
1743  updateAllVectorLayersAtCurrentFrame();
1744 }
1745 
1746 
1747 void ScribbleArea::floodFillError( int errorType )
1748 {
1749  QString message, error;
1750  if ( errorType == 1 ) { message = tr( "There is a gap in your drawing (or maybe you have zoomed too much)." ); }
1751  if ( errorType == 2 || errorType == 3 ) message = tr( "Sorry! This doesn't always work."
1752  "Please try again (zoom a bit, click at another location... )<br>"
1753  "if it doesn't work, zoom a bit and check that your paths are connected by pressing F1.)." );
1754 
1755  if ( errorType == 1 ) { error = tr( "Out of bound." ); }
1756  if ( errorType == 2 ) { error = tr( "Could not find a closed path." ); }
1757  if ( errorType == 3 ) { error = tr( "Could not find the root index." ); }
1758  QMessageBox::warning( this, tr( "Flood fill error" ), tr("%1<br><br>Error: %2").arg(message).arg(error), QMessageBox::Ok, QMessageBox::Ok );
1759  deselectAll();
1760 }
QPixmap quickSizeCursor()
precision circular cursor: used for drawing stroke size while adjusting
Definition: basetool.cpp:179
Definition: layer.h:32
void resetSelectionProperties()
ScribbleArea::resetSelectionProperties should be used whenever translate, rotate, transform...
QVector< QPoint > calcSelectionCenterPoints()
ScribbleArea::calculateSelectionCenter.
QPixmap canvasCursor()
precision circular cursor: used for drawing a cursor within scribble area.
Definition: basetool.cpp:121
void flipSelection(bool flipVertical)
ScribbleArea::flipSelection flip selection along the X or Y axis.
Definition: object.h:71