Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
object.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 <QDomDocument>
19 #include <QTextStream>
20 #include <QMessageBox>
21 #include <QProgressDialog>
22 #include <QApplication>
23 
24 #include "object.h"
25 #include "layer.h"
26 #include "layerbitmap.h"
27 #include "layervector.h"
28 #include "layersound.h"
29 #include "layercamera.h"
30 
31 //#include "flash.h"
32 #include "util.h"
33 #include "editor.h"
34 #include "bitmapimage.h"
35 #include "fileformat.h"
36 
37 // ******* Mac-specific: ******** (please comment (or reimplement) the lines below to compile on Windows or Linux
38 //#include <CoreFoundation/CoreFoundation.h>
39 // ******************************
40 
41 Object::Object( QObject* parent ) : QObject( parent )
42 {
43  setData( new ObjectData() );
44 }
45 
46 Object::~Object()
47 {
48  while ( !mLayers.empty() )
49  {
50  delete mLayers.takeLast();
51  }
52 
53  // Delete the working directory if this is not a "New" project.
54  if(!filePath().isEmpty())
55  {
56  deleteWorkingDir();
57  }
58 }
59 
60 void Object::init()
61 {
62  mEditorState.reset( new ObjectData );
63 
64  createWorkingDir();
65 
66  // default layers
67  addNewCameraLayer();
68  addNewVectorLayer();
69  addNewBitmapLayer();
70 
71  // default palette
72  loadDefaultPalette();
73 }
74 
75 QDomElement Object::saveXML( QDomDocument& doc )
76 {
77  qDebug() << dataDir();
78  qDebug() << workingDir();
79  qDebug() << mainXMLFile();
80 
81  QDomElement tag = doc.createElement( "object" );
82 
83  for ( int i = 0; i < getLayerCount(); i++ )
84  {
85  Layer* layer = getLayer( i );
86  QDomElement layerTag = layer->createDomElement( doc );
87  tag.appendChild( layerTag );
88  }
89  return tag;
90 }
91 
92 bool Object::loadXML( QDomElement docElem, ProgressCallback progress )
93 {
94  if ( docElem.isNull() )
95  {
96  return false;
97  }
98  int layerNumber = -1;
99 
100  const QString dataDirPath = mDataDirPath;
101 
102  int allNodesCount = docElem.childNodes().count();
103  int processedNodeCount = 0;
104 
105  for ( QDomNode node = docElem.firstChild(); !node.isNull(); node = node.nextSibling() )
106  {
107  processedNodeCount += 1;
108  progress( (float)processedNodeCount / allNodesCount );
109 
110  QDomElement element = node.toElement(); // try to convert the node to an element.
111  if ( element.tagName() == "layer" )
112  {
113  if ( element.attribute( "type" ).toInt() == Layer::BITMAP )
114  {
115  addNewBitmapLayer();
116  layerNumber++;
117  getLayer( layerNumber )->loadDomElement( element, dataDirPath );
118  }
119  else if ( element.attribute( "type" ).toInt() == Layer::VECTOR )
120  {
121  addNewVectorLayer();
122  layerNumber++;
123  getLayer( layerNumber )->loadDomElement( element, dataDirPath );
124  }
125  else if ( element.attribute( "type" ).toInt() == Layer::SOUND )
126  {
127  addNewSoundLayer();
128  layerNumber++;
129  getLayer( layerNumber )->loadDomElement( element, dataDirPath );
130  }
131  else if ( element.attribute( "type" ).toInt() == Layer::CAMERA )
132  {
133  addNewCameraLayer();
134  layerNumber++;
135  getLayer( layerNumber )->loadDomElement( element, dataDirPath );
136  }
137  }
138  }
139  return true;
140 }
141 
142 LayerBitmap* Object::addNewBitmapLayer()
143 {
144  LayerBitmap* layerBitmap = new LayerBitmap( this );
145  mLayers.append( layerBitmap );
146 
147  layerBitmap->addNewEmptyKeyAt( 1 );
148 
149  return layerBitmap;
150 }
151 
152 LayerVector* Object::addNewVectorLayer()
153 {
154  LayerVector* layerVector = new LayerVector( this );
155  mLayers.append( layerVector );
156 
157  layerVector->addNewEmptyKeyAt( 1 );
158 
159  return layerVector;
160 }
161 
162 LayerSound* Object::addNewSoundLayer()
163 {
164  LayerSound* layerSound = new LayerSound( this );
165  mLayers.append( layerSound );
166 
167  // No default keyFrame at position 1 for Sound layer.
168 
169  return layerSound;
170 }
171 
172 LayerCamera* Object::addNewCameraLayer()
173 {
174  LayerCamera* layerCamera = new LayerCamera( this );
175  mLayers.append( layerCamera );
176 
177  layerCamera->addNewEmptyKeyAt( 1 );
178 
179  return layerCamera;
180 }
181 
182 void Object::createWorkingDir()
183 {
184  QString strFolderName;
185  if ( mFilePath.isEmpty() )
186  {
187  strFolderName = "Default";
188  }
189  else
190  {
191  QFileInfo fileInfo( mFilePath );
192  strFolderName = fileInfo.completeBaseName();
193  }
194  QString strWorkingDir = QDir::tempPath()
195  + "/Pencil2D/"
196  + strFolderName
197  + PFF_TMP_DECOMPRESS_EXT
198  + "/";
199 
200  QDir dir( QDir::tempPath() );
201  dir.mkpath( strWorkingDir );
202 
203  mWorkingDirPath = strWorkingDir;
204 
205  QDir dataDir( strWorkingDir + PFF_DATA_DIR );
206  dataDir.mkpath( "." );
207 
208  mDataDirPath = dataDir.absolutePath();
209 }
210 
211 void Object::deleteWorkingDir() const
212 {
213  QDir dataDir(mWorkingDirPath);
214  dataDir.removeRecursively();
215 }
216 
217 int Object::getMaxLayerID()
218 {
219  int maxId = 0;
220  for ( Layer* iLayer : mLayers )
221  {
222  if ( iLayer->id() > maxId )
223  {
224  maxId = iLayer->id();
225  }
226  }
227  return maxId;
228 }
229 
230 int Object::getUniqueLayerID()
231 {
232  return 1 + getMaxLayerID();
233 }
234 
235 Layer* Object::getLayer( int i ) const
236 {
237  if ( i < 0 || i >= getLayerCount() )
238  {
239  return nullptr;
240  }
241 
242  return mLayers.at( i );
243 }
244 
245 Layer* Object::findLayerByName( QString strName, Layer::LAYER_TYPE type ) const
246 {
247  bool bCheckType = ( type != Layer::UNDEFINED );
248 
249  for ( Layer* layer : mLayers )
250  {
251  bool bTypeMatch = ( bCheckType ) ? ( type == layer->type() ): true ;
252 
253  if ( layer->name() == strName && bTypeMatch )
254  {
255  return layer;
256  }
257  }
258  return nullptr;
259 }
260 
261 bool Object::moveLayer( int i, int j )
262 {
263  if ( i< 0 || i >= mLayers.size() )
264  {
265  return false;
266  }
267 
268  if ( j < 0 || j >= mLayers.size() )
269  {
270  return false;
271  }
272 
273  if ( i != j )
274  {
275  Layer* tmp = mLayers.at( i );
276  mLayers[ i ] = mLayers.at( j );
277  mLayers[ j ] = tmp;
278  }
279  return true;
280 }
281 
282 void Object::deleteLayer( int i )
283 {
284  if ( i > -1 && i < mLayers.size() )
285  {
286  disconnect( mLayers[ i ], 0, 0, 0 ); // disconnect the layer from this object
287  delete mLayers.takeAt( i );
288  }
289 }
290 
291 void Object::deleteLayer( Layer* layer )
292 {
293  auto it = std::find( mLayers.begin(), mLayers.end(), layer );
294 
295  if ( it != mLayers.end() )
296  {
297  disconnect( layer, 0, 0, 0 );
298  delete layer;
299  mLayers.erase( it );
300  }
301 }
302 
303 ColourRef Object::getColour( int i )
304 {
305  ColourRef result( Qt::white, "error" );
306  if ( i > -1 && i < mPalette.size() )
307  {
308  result = mPalette.at( i );
309  }
310  return result;
311 }
312 
313 void Object::addColour( QColor colour )
314 {
315  addColour( ColourRef( colour, "Colour " + QString::number( mPalette.size() ) ) );
316 }
317 
318 bool Object::removeColour( int index )
319 {
320  for ( int i = 0; i < getLayerCount(); i++ )
321  {
322  Layer* layer = getLayer( i );
323  if ( layer->type() == Layer::VECTOR )
324  {
325  LayerVector* layerVector = ( ( LayerVector* )layer );
326  if ( layerVector->usesColour( index ) ) return false;
327  }
328  }
329  for ( int i = 0; i < getLayerCount(); i++ )
330  {
331  Layer* layer = getLayer( i );
332  if ( layer->type() == Layer::VECTOR )
333  {
334  LayerVector* layerVector = ( ( LayerVector* )layer );
335  layerVector->removeColour( index );
336  }
337  }
338  mPalette.removeAt( index );
339  return true;
340  // update the vector pictures using that colour !
341 }
342 
343 void Object::renameColour( int i, QString text )
344 {
345  mPalette[ i ].name = text;
346 }
347 
348 bool Object::savePalette( QString filePath )
349 {
350  return exportPalette( filePath + "/palette.xml" );
351 }
352 
353 bool Object::exportPalette( QString filePath )
354 {
355  QFile* file = new QFile( filePath );
356  if ( !file->open( QFile::WriteOnly | QFile::Text ) )
357  {
358  //QMessageBox::warning(this, "Warning", "Cannot write file");
359  return false;
360  }
361  QTextStream out( file );
362 
363  QDomDocument doc( "PencilPalette" );
364  QDomElement root = doc.createElement( "palette" );
365  doc.appendChild( root );
366  for ( int i = 0; i < mPalette.size(); i++ )
367  {
368  QDomElement tag = doc.createElement( "Colour" );
369  tag.setAttribute( "name", mPalette.at( i ).name );
370  tag.setAttribute( "red", mPalette.at( i ).colour.red() );
371  tag.setAttribute( "green", mPalette.at( i ).colour.green() );
372  tag.setAttribute( "blue", mPalette.at( i ).colour.blue() );
373  root.appendChild( tag );
374  //QDomText t = doc.createTextNode( myPalette.at(i).name );
375  //tag.appendChild(t);
376  }
377  //QString xml = doc.toString();
378 
379  int IndentSize = 2;
380  doc.save( out, IndentSize );
381  return true;
382 }
383 
384 bool Object::importPalette( QString filePath )
385 {
386  QFile* file = new QFile( filePath );
387  if ( !file->open( QFile::ReadOnly ) )
388  {
389  //QMessageBox::warning(this, "Warning", "Cannot read file");
390  return false;
391  }
392 
393  QDomDocument doc;
394  doc.setContent( file );
395 
396  mPalette.clear();
397  QDomElement docElem = doc.documentElement();
398  QDomNode tag = docElem.firstChild();
399  while ( !tag.isNull() )
400  {
401  QDomElement e = tag.toElement(); // try to convert the node to an element.
402  if ( !e.isNull() )
403  {
404  QString name = e.attribute( "name" );
405  int r = e.attribute( "red" ).toInt();
406  int g = e.attribute( "green" ).toInt();
407  int b = e.attribute( "blue" ).toInt();
408  mPalette.append( ColourRef( QColor( r, g, b ), name ) );
409  //qDebug() << name << r << g << b << endl; // the node really is an element.
410  }
411  tag = tag.nextSibling();
412  }
413  return true;
414 }
415 
416 
417 void Object::loadDefaultPalette()
418 {
419  mPalette.clear();
420  addColour( ColourRef( QColor( Qt::black ), QString( tr( "Black" ) ) ) );
421  addColour( ColourRef( QColor( Qt::red ), QString( tr( "Red" ) ) ) );
422  addColour( ColourRef( QColor( Qt::darkRed ), QString( tr( "Dark Red" ) ) ) );
423  addColour( ColourRef( QColor( 255, 128, 0 ), QString( tr( "Orange" ) ) ) );
424  addColour( ColourRef( QColor( 128, 64, 0 ), QString( tr( "Dark Orange" ) ) ) );
425  addColour( ColourRef( QColor( Qt::yellow ), QString( tr( "Yellow" ) ) ) );
426  addColour( ColourRef( QColor( Qt::darkYellow ), QString( tr( "Dark Yellow" ) ) ) );
427  addColour( ColourRef( QColor( Qt::green ), QString( tr( "Green" ) ) ) );
428  addColour( ColourRef( QColor( Qt::darkGreen ), QString( tr( "Dark Green" ) ) ) );
429  addColour( ColourRef( QColor( Qt::cyan ), QString( tr( "Cyan" ) ) ) );
430  addColour( ColourRef( QColor( Qt::darkCyan ), QString( tr( "Dark Cyan" ) ) ) );
431  addColour( ColourRef( QColor( Qt::blue ), QString( tr( "Blue" ) ) ) );
432  addColour( ColourRef( QColor( Qt::darkBlue ), QString( tr( "Dark Blue" ) ) ) );
433  addColour( ColourRef( QColor( 255, 255, 255 ), QString( tr( "White" ) ) ) );
434  addColour( ColourRef( QColor( 220, 220, 229 ), QString( tr( "Very Light Grey" ) ) ) );
435  addColour( ColourRef( QColor( Qt::lightGray ), QString( tr( "Light Grey" ) ) ) );
436  addColour( ColourRef( QColor( Qt::gray ), QString( tr( "Grey" ) ) ) );
437  addColour( ColourRef( QColor( Qt::darkGray ), QString( tr( "Dark Grey" ) ) ) );
438  addColour( ColourRef( QColor( 255, 227, 187 ), QString( tr( "Light Skin" ) ) ) );
439  addColour( ColourRef( QColor( 221, 196, 161 ), QString( tr( "Light Skin - shade" ) ) ) );
440  addColour( ColourRef( QColor( 255, 214, 156 ), QString( tr( "Skin" ) ) ) );
441  addColour( ColourRef( QColor( 207, 174, 127 ), QString( tr( "Skin - shade" ) ) ) );
442  addColour( ColourRef( QColor( 255, 198, 116 ), QString( tr( "Dark Skin" ) ) ) );
443  addColour( ColourRef( QColor( 227, 177, 105 ), QString( tr( "Dark Skin - shade" ) ) ) );
444 }
445 
446 void Object::paintImage( QPainter& painter, int frameNumber,
447  bool background,
448  bool antialiasing ) const
449 {
450  painter.setRenderHint( QPainter::Antialiasing, true );
451  painter.setRenderHint( QPainter::SmoothPixmapTransform, true );
452 
453  //painter.setTransform(matrix);
454  painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
455 
456  // paints the background
457  if ( background )
458  {
459  painter.setPen( Qt::NoPen );
460  painter.setBrush( Qt::white );
461  painter.setWorldMatrixEnabled( false );
462  painter.drawRect( QRect( 0, 0, painter.device()->width(), painter.device()->height() ) );
463  painter.setWorldMatrixEnabled( true );
464  }
465 
466  for ( int i = 0; i < getLayerCount(); i++ )
467  {
468  Layer* layer = getLayer( i );
469  if ( layer->mVisible )
470  {
471  painter.setOpacity( 1.0 );
472 
473  // paints the bitmap images
474  if ( layer->type() == Layer::BITMAP )
475  {
476  LayerBitmap* layerBitmap = ( LayerBitmap* )layer;
477  layerBitmap->getLastBitmapImageAtFrame( frameNumber, 0 )->paintImage( painter );
478  }
479  // paints the vector images
480  if ( layer->type() == Layer::VECTOR )
481  {
482  LayerVector* layerVector = ( LayerVector* )layer;
483  layerVector->getLastVectorImageAtFrame( frameNumber, 0 )->paintImage( painter,
484  false,
485  false,
486  antialiasing );
487  }
488  }
489  }
490 }
491 
492 QString Object::copyFileToDataFolder( QString strFilePath )
493 {
494  if ( !QFile::exists( strFilePath ) )
495  {
496  qDebug() << "[Object] sound file doesn't exist: " << strFilePath;
497  return "";
498  }
499 
500  QString sNewFileName = "sound_";
501  sNewFileName += QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss_zzz.");
502  sNewFileName += QFileInfo( strFilePath ).suffix();
503 
504  QString srcFile = strFilePath;
505  QString destFile = QDir( mDataDirPath ).filePath( sNewFileName );
506 
507  if ( QFile::exists( destFile ) )
508  {
509  QFile::remove( destFile );
510  }
511 
512  bool bCopyOK = QFile::copy( srcFile, destFile );
513  if ( !bCopyOK )
514  {
515  qDebug() << "[Object] couldn't copy sound file to data folder: " << strFilePath;
516  return "";
517  }
518 
519  return destFile;
520 }
521 
522 bool Object::exportFrames( int frameStart, int frameEnd,
523  Layer* currentLayer,
524  QSize exportSize, QString filePath,
525  const char* format,
526  int quality,
527  bool transparency,
528  bool antialiasing,
529  QProgressDialog* progress = NULL,
530  int progressMax = 50 )
531 {
532  QSettings settings( PENCIL2D, PENCIL2D );
533 
534  QString extension = "";
535  QString formatStr = format;
536  if ( formatStr == "PNG" || formatStr == "png" )
537  {
538  format = "PNG";
539  extension = ".png";
540  }
541  if ( formatStr == "JPG" || formatStr == "jpg" || formatStr == "JPEG" || formatStr == "jpeg" )
542  {
543  format = "JPG";
544  extension = ".jpg";
545  transparency = false; // JPG doesn't support transparency so we have to include the background
546  }
547  if ( filePath.endsWith( extension, Qt::CaseInsensitive ) )
548  {
549  filePath.chop( extension.size() );
550  }
551 
552  qDebug() << "Exporting frames from "
553  << frameStart << "to"
554  << frameEnd
555  << "at size " << exportSize;
556 
557  for ( int currentFrame = frameStart; currentFrame <= frameEnd; currentFrame++ )
558  {
559  if ( progress != NULL )
560  {
561  int totalFramesToExport = ( frameEnd - frameStart ) + 1;
562  if ( totalFramesToExport != 0 ) // Avoid dividing by zero.
563  {
564  progress->setValue( ( currentFrame - frameStart + 1 )*progressMax / totalFramesToExport );
565  QApplication::processEvents(); // Required to make progress bar update on-screen.
566  }
567 
568  if(progress->wasCanceled())
569  {
570  break;
571  }
572  }
573 
574  QImage imageToExport( exportSize, QImage::Format_ARGB32_Premultiplied );
575  QColor bgColor = Qt::white;
576  if (transparency) {
577  bgColor.setAlpha(0);
578  }
579  imageToExport.fill(bgColor);
580 
581  QPainter painter( &imageToExport );
582 
583  QRect viewRect;
584  if(currentLayer != nullptr)
585  {
586  viewRect = ( ( LayerCamera* )currentLayer )->getViewRect();
587  }
588  else
589  {
590  // Some old .PCL files may not have a camera layer.
591  // In that case, use a default size.
592  viewRect = QRect( QPoint(-320,-240), QSize(640,480) );
593  }
594 
595  QTransform mapView = RectMapTransform( viewRect, QRectF( QPointF( 0, 0 ), exportSize ) );
596 // mapView = ( ( LayerCamera* )currentLayer )->getViewAtFrame( currentFrame ) * mapView;
597  painter.setWorldTransform( mapView );
598 
599  paintImage( painter, currentFrame, false, antialiasing );
600 
601  QString frameNumberString = QString::number( currentFrame );
602  while ( frameNumberString.length() < 4 )
603  {
604  frameNumberString.prepend( "0" );
605  }
606  imageToExport.save( filePath + frameNumberString + extension, format, quality );
607  }
608 
609  return true;
610 }
611 
612 
613 void convertNFrames( int fps, int exportFps, int* frameRepeat, int* frameReminder, int* framePutEvery, int* frameSkipEvery )
614 {
616  *frameRepeat = exportFps / fps; // identic frames to export per frame
617  *frameReminder = exportFps % fps; // additional frames to export in an fps cycle (= 1 second)
618 
620  if ( *frameReminder == 0 )
621  {
622  *framePutEvery = 0; *frameSkipEvery = 0;
623  } // so, frameSkipEvery and framePutEvery will not be used.
624  else if ( *frameReminder > ( fps - *frameReminder ) )
625  {
626  *frameSkipEvery = fps / ( fps - *frameReminder ); *framePutEvery = 0;
627  }
628  else
629  {
630  *framePutEvery = fps / *frameReminder; *frameSkipEvery = 0;
631  }
632  qDebug() << "-->convertedNFrames";
633 }
634 
635 
636 bool Object::exportX( int frameStart, int frameEnd, QTransform view, QSize exportSize, QString filePath, bool antialiasing )
637 {
638  QSettings settings( PENCIL2D, PENCIL2D );
639 
640  int page;
641  page = 0;
642  for ( int j = frameStart; j <= frameEnd; j = j + 15 )
643  {
644  QImage xImg( QSize( 2300, 3400 ), QImage::Format_ARGB32_Premultiplied );
645  QPainter xPainter( &xImg );
646  xPainter.fillRect( 0, 0, 2300, 3400, Qt::white );
647  int y = j - 1;
648  for ( int i = j; i < 15 + page * 15 && i <= frameEnd; i++ )
649  {
650  QRect source = QRect( QPoint( 0, 0 ), exportSize );
651  QRect target = QRect( QPoint( ( y % 3 ) * 800 + 30, ( y / 3 ) * 680 + 50 - page * 3400 ), QSize( 640, 480 ) );
652  QTransform thumbView = view * RectMapTransform( source, target );
653  xPainter.setWorldTransform( thumbView );
654  xPainter.setClipRegion( thumbView.inverted().map( QRegion( target ) ) );
655  paintImage( xPainter, i, false, antialiasing );
656  xPainter.resetMatrix();
657  xPainter.setClipping( false );
658  xPainter.setPen( Qt::black );
659  xPainter.setFont( QFont( "helvetica", 50 ) );
660  xPainter.drawRect( target );
661  xPainter.drawText( QPoint( ( y % 3 ) * 800 + 35, ( y / 3 ) * 680 + 65 - page * 3400 ), QString::number( i ) );
662  y++;
663  }
664 
665  if ( filePath.endsWith( ".jpg", Qt::CaseInsensitive ) )
666  {
667  filePath.chop( 4 );
668  }
669  if ( !xImg.save( filePath + QString::number( page ) + ".jpg", "JPG", 60 ) ) {
670  return false;
671  }
672  page++;
673  }
674 
675  return true;
676 }
677 
678 bool Object::exportIm( int frameStart, int frameEnd, QTransform view, QSize exportSize, QString filePath, QString format, bool antialiasing, bool transparency )
679 {
680  Q_UNUSED( frameEnd );
681  QImage imageToExport( exportSize, QImage::Format_ARGB32_Premultiplied );
682 
683  QColor bgColor = Qt::white;
684  if (transparency) {
685  bgColor.setAlpha(0);
686  }
687  imageToExport.fill(bgColor);
688 
689  QPainter painter( &imageToExport );
690  painter.setWorldTransform( view );
691  paintImage( painter, frameStart, false, antialiasing );
692 
693  return imageToExport.save( filePath, format.toStdString().c_str() );
694 }
695 
696 int Object::getLayerCount() const
697 {
698  return mLayers.size();
699 }
700 
701 ObjectData* Object::data()
702 {
703  Q_ASSERT( mEditorState != nullptr );
704  return mEditorState.get();
705 }
706 
707 void Object::setData( ObjectData* d )
708 {
709  Q_ASSERT( d != nullptr );
710  mEditorState.reset( d );
711 }
712 
713 void Object::setLayerUpdated(int layerId)
714 {
715  emit layerChanged(layerId);
716 }
Definition: layer.h:32