All Classes Namespaces Functions Variables Enumerations Properties Pages
object.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 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 #include "object.h"
18 
19 #include <QDomDocument>
20 #include <QTextStream>
21 #include <QProgressDialog>
22 #include <QApplication>
23 #include <QFile>
24 #include <QFileInfo>
25 #include <QDir>
26 #include <QDebug>
27 #include <QDateTime>
28 #include <QSettings>
29 
30 #include "layer.h"
31 #include "layerbitmap.h"
32 #include "layervector.h"
33 #include "layersound.h"
34 #include "layercamera.h"
35 
36 #include "util.h"
37 #include "editor.h"
38 #include "bitmapimage.h"
39 #include "vectorimage.h"
40 #include "fileformat.h"
41 #include "activeframepool.h"
42 
43 
44 Object::Object(QObject* parent) : QObject(parent)
45 {
46  setData(new ObjectData());
47  mActiveFramePool.reset(new ActiveFramePool);
48 }
49 
50 Object::~Object()
51 {
52  mActiveFramePool->clear();
53 
54  for (Layer* layer : mLayers)
55  delete layer;
56  mLayers.clear();
57 
58  deleteWorkingDir();
59 }
60 
61 void Object::init()
62 {
63  mData.reset(new ObjectData);
64 
65  createWorkingDir();
66 
67  // default palette
68  loadDefaultPalette();
69 }
70 
71 QDomElement Object::saveXML(QDomDocument& doc) const
72 {
73  QDomElement objectTag = doc.createElement("object");
74 
75  for (int i = 0; i < getLayerCount(); i++)
76  {
77  Layer* layer = getLayer(i);
78  QDomElement layerTag = layer->createDomElement(doc);
79  objectTag.appendChild(layerTag);
80  }
81  return objectTag;
82 }
83 
84 bool Object::loadXML(QDomElement docElem, ProgressCallback progressForward)
85 {
86  if (docElem.isNull())
87  {
88  return false;
89  }
90 
91  const QString dataDirPath = mDataDirPath;
92 
93  for (QDomNode node = docElem.firstChild(); !node.isNull(); node = node.nextSibling())
94  {
95  QDomElement element = node.toElement(); // try to convert the node to an element.
96  if (element.tagName() == "layer")
97  {
98  Layer* newLayer;
99  switch (element.attribute("type").toInt())
100  {
101  case Layer::BITMAP:
102  newLayer = new LayerBitmap(this);
103  break;
104  case Layer::VECTOR:
105  newLayer = new LayerVector(this);
106  break;
107  case Layer::SOUND:
108  newLayer = new LayerSound(this);
109  break;
110  case Layer::CAMERA:
111  newLayer = new LayerCamera(this);
112  break;
113  default: Q_ASSERT(false); continue;
114  }
115  mLayers.append(newLayer);
116  newLayer->loadDomElement(element, dataDirPath, progressForward);
117  }
118  }
119  return true;
120 }
121 
122 LayerBitmap* Object::addNewBitmapLayer()
123 {
124  LayerBitmap* layerBitmap = new LayerBitmap(this);
125  mLayers.append(layerBitmap);
126 
127  layerBitmap->addNewKeyFrameAt(1);
128 
129  return layerBitmap;
130 }
131 
132 LayerVector* Object::addNewVectorLayer()
133 {
134  LayerVector* layerVector = new LayerVector(this);
135  mLayers.append(layerVector);
136 
137  layerVector->addNewKeyFrameAt(1);
138 
139  return layerVector;
140 }
141 
142 LayerSound* Object::addNewSoundLayer()
143 {
144  LayerSound* layerSound = new LayerSound(this);
145  mLayers.append(layerSound);
146 
147  // No default keyFrame at position 1 for Sound layer.
148 
149  return layerSound;
150 }
151 
152 LayerCamera* Object::addNewCameraLayer()
153 {
154  LayerCamera* layerCamera = new LayerCamera(this);
155  mLayers.append(layerCamera);
156 
157  layerCamera->addNewKeyFrameAt(1);
158 
159  connect(layerCamera, &LayerCamera::resolutionChanged, this, &Object::layerViewChanged);
160 
161  return layerCamera;
162 }
163 
164 void Object::createWorkingDir()
165 {
166  QString projectName;
167  if (mFilePath.isEmpty())
168  {
169  projectName = "Default";
170  }
171  else
172  {
173  QFileInfo fileInfo(mFilePath);
174  projectName = fileInfo.completeBaseName();
175  }
176  QDir dir(QDir::tempPath());
177 
178  QString strWorkingDir;
179  do
180  {
181  strWorkingDir = QString("%1/Pencil2D/%2_%3_%4/")
182  .arg(QDir::tempPath())
183  .arg(projectName)
184  .arg(PFF_TMP_DECOMPRESS_EXT)
185  .arg(uniqueString(8));
186  }
187  while(dir.exists(strWorkingDir));
188 
189  dir.mkpath(strWorkingDir);
190  mWorkingDirPath = strWorkingDir;
191 
192  QDir dataDir(strWorkingDir + PFF_DATA_DIR);
193  dataDir.mkpath(".");
194 
195  mDataDirPath = dataDir.absolutePath();
196 }
197 
198 void Object::deleteWorkingDir() const
199 {
200  if (!mWorkingDirPath.isEmpty())
201  {
202  QDir dir(mWorkingDirPath);
203  bool ok = dir.removeRecursively();
204  Q_ASSERT(ok);
205  }
206 }
207 
208 void Object::setWorkingDir(const QString& path)
209 {
210  QDir dir(path);
211  Q_ASSERT(dir.exists());
212  mWorkingDirPath = path;
213 }
214 
215 void Object::createDefaultLayers()
216 {
217  // default layers
218  addNewCameraLayer();
219  addNewVectorLayer();
220  addNewBitmapLayer();
221 }
222 
223 int Object::getMaxLayerID()
224 {
225  int maxId = 0;
226  for (Layer* iLayer : mLayers)
227  {
228  if (iLayer->id() > maxId)
229  {
230  maxId = iLayer->id();
231  }
232  }
233  return maxId;
234 }
235 
236 int Object::getUniqueLayerID()
237 {
238  return 1 + getMaxLayerID();
239 }
240 
241 Layer* Object::getLayer(int i) const
242 {
243  if (i < 0 || i >= getLayerCount())
244  {
245  return nullptr;
246  }
247 
248  return mLayers.at(i);
249 }
250 
251 Layer* Object::findLayerByName(QString strName, Layer::LAYER_TYPE type) const
252 {
253  bool bCheckType = (type != Layer::UNDEFINED);
254  for (Layer* layer : mLayers)
255  {
256  bool isTypeMatch = (bCheckType) ? (type == layer->type()) : true;
257  if (isTypeMatch && layer->name() == strName)
258  {
259  return layer;
260  }
261  }
262  return nullptr;
263 }
264 
265 Layer* Object::takeLayer(int layerId)
266 {
267  // Removes the layer from this Object and returns it
268  // The ownership of this layer has been transfer to the caller
269  int index = -1;
270  for (int i = 0; i< mLayers.length(); ++i)
271  {
272  Layer* layer = mLayers[i];
273  if (layer->id() == layerId)
274  {
275  index = i;
276  break;
277  }
278  }
279 
280  if (index == -1) { return nullptr; }
281 
282  Layer* layer = mLayers.takeAt(index);
283  layer->setParent(nullptr);
284  return layer;
285 }
286 
287 bool Object::swapLayers(int i, int j)
288 {
289  if (i < 0 || i >= mLayers.size())
290  {
291  return false;
292  }
293 
294  if (j < 0 || j >= mLayers.size())
295  {
296  return false;
297  }
298 
299  if (i != j)
300  {
301  Layer* tmp = mLayers.at(i);
302  mLayers[i] = mLayers.at(j);
303  mLayers[j] = tmp;
304  }
305  return true;
306 }
307 
308 void Object::deleteLayer(int i)
309 {
310  if (i > -1 && i < mLayers.size())
311  {
312  delete mLayers.takeAt(i);
313  }
314 }
315 
316 void Object::deleteLayer(Layer* layer)
317 {
318  auto it = std::find(mLayers.begin(), mLayers.end(), layer);
319 
320  if (it != mLayers.end())
321  {
322  delete layer;
323  mLayers.erase(it);
324  }
325 }
326 
327 bool Object::addLayer(Layer* layer)
328 {
329  if (layer == nullptr)
330  {
331  return false;
332  }
333  if (mLayers.contains(layer))
334  {
335  return false;
336  }
337  layer->setObject(this);
338  mLayers.append(layer);
339  return true;
340 }
341 
342 ColorRef Object::getColor(int index) const
343 {
344  ColorRef result(Qt::white, tr("error"));
345  if (index > -1 && index < mPalette.size())
346  {
347  result = mPalette.at(index);
348  }
349  return result;
350 }
351 
352 void Object::setColor(int index, QColor newColor)
353 {
354  Q_ASSERT(index >= 0);
355 
356  mPalette[index].color = newColor;
357 }
358 
359 void Object::setColorRef(int index, ColorRef newColorRef)
360 {
361  mPalette[index] = newColorRef;
362 }
363 
364 void Object::addColor(QColor color)
365 {
366  addColor(ColorRef(color, tr("Color %1").arg(QString::number(mPalette.size()))));
367 }
368 
369 void Object::movePaletteColor(int start, int end)
370 {
371  mPalette.move(start, end);
372 }
373 
374 void Object::moveVectorColor(int start, int end)
375 {
376  for (int i = 0; i < getLayerCount(); i++)
377  {
378  Layer* layer = getLayer(i);
379  if (layer->type() == Layer::VECTOR)
380  {
381  static_cast<LayerVector*>(layer)->moveColor(start, end);
382  }
383  }
384 }
385 
386 void Object::addColorAtIndex(int index, ColorRef newColor)
387 {
388  mPalette.insert(index, newColor);
389 }
390 
391 bool Object::isColorInUse(int index)
392 {
393  for (int i = 0; i < getLayerCount(); i++)
394  {
395  Layer* layer = getLayer(i);
396  if (layer->type() == Layer::VECTOR)
397  {
398  LayerVector* layerVector = static_cast<LayerVector*>(layer);
399 
400  if (layerVector->usesColor(index))
401  {
402  return true;
403  }
404  }
405  }
406  return false;
407 
408 }
409 
410 void Object::removeColor(int index)
411 {
412  for (int i = 0; i < getLayerCount(); i++)
413  {
414  Layer* layer = getLayer(i);
415  if (layer->type() == Layer::VECTOR)
416  {
417  LayerVector* layerVector = static_cast<LayerVector*>(layer);
418  layerVector->removeColor(index);
419  }
420  }
421 
422  mPalette.removeAt(index);
423 
424  // update the vector pictures using that color !
425 }
426 
427 void Object::renameColor(int i, QString text)
428 {
429  mPalette[i].name = text;
430 }
431 
432 QString Object::savePalette(const QString& dataFolder) const
433 {
434  QString fullPath = QDir(dataFolder).filePath("palette.xml");
435  bool ok = exportPalette(fullPath);
436  if (ok)
437  return fullPath;
438  return "";
439 }
440 
441 void Object::exportPaletteGPL(QFile& file) const
442 {
443  QString fileName = QFileInfo(file).baseName();
444  QTextStream out(&file);
445 
446  out << "GIMP Palette" << "\n";
447  out << "Name: " << fileName << "\n";
448  out << "#" << "\n";
449 
450  for (ColorRef ref : mPalette)
451  {
452  QColor toRgb = ref.color.toRgb();
453  out << QString("%1 %2 %3").arg(toRgb.red()).arg(toRgb.green()).arg(toRgb.blue());
454  out << " " << ref.name << "\n";
455  }
456 }
457 
458 void Object::exportPalettePencil(QFile& file) const
459 {
460  QTextStream out(&file);
461 
462  QDomDocument doc("PencilPalette");
463  QDomElement root = doc.createElement("palette");
464  doc.appendChild(root);
465  for (int i = 0; i < mPalette.size(); i++)
466  {
467  ColorRef ref = mPalette.at(i);
468  QDomElement tag = doc.createElement("Color");
469  tag.setAttribute("name", ref.name);
470  tag.setAttribute("red", ref.color.red());
471  tag.setAttribute("green", ref.color.green());
472  tag.setAttribute("blue", ref.color.blue());
473  tag.setAttribute("alpha", ref.color.alpha());
474  root.appendChild(tag);
475  }
476  int IndentSize = 2;
477  doc.save(out, IndentSize);
478 }
479 
480 bool Object::exportPalette(const QString& filePath) const
481 {
482  QFile file(filePath);
483  if (!file.open(QFile::WriteOnly | QFile::Text))
484  {
485  qDebug("Error: cannot export palette");
486  return false;
487  }
488 
489  if (file.fileName().endsWith(".gpl", Qt::CaseInsensitive))
490  exportPaletteGPL(file);
491  else
492  exportPalettePencil(file);
493 
494  file.close();
495  return true;
496 }
497 
498 /* Import the .gpl GIMP palette format.
499  *
500  * This functions supports importing both the old and new .gpl formats.
501  * This should load colors the same as GIMP, with the following intentional exceptions:
502  * - Whitespace before and after a name does not appear in the name
503  * - The last line is processed, even if there is not a trailing newline
504  * - Colors without a name will use our automatic naming system rather than "Untitled"
505  */
506 void Object::importPaletteGPL(QFile& file)
507 {
508  QTextStream in(&file);
509  QString line;
510 
511  // First line must start with "GIMP Palette"
512  // Displaying an error here would be nice
513  in.readLineInto(&line);
514  if (!line.startsWith("GIMP Palette")) return;
515 
516  in.readLineInto(&line);
517 
518  // There are two GPL formats, the new one must start with "Name: " on the second line
519  if (line.startsWith("Name: "))
520  {
521  in.readLineInto(&line);
522  // The new format contains an optional thrid line starting with "Columns: "
523  if (line.startsWith("Columns: "))
524  {
525  // Skip to next line
526  in.readLineInto(&line);
527  }
528  }
529 
530  // Colors inherit the value from the previous color for missing channels
531  // Some palettes may rely on this behavior so we should try to replicate it
532  QColor prevColor(Qt::black);
533 
534  do
535  {
536  // Ignore comments and empty lines
537  if (line.isEmpty() || line.startsWith("#")) continue;
538 
539  int red = 0;
540  int green = 0;
541  int blue = 0;
542 
543  int countInLine = 0;
544  QString name = "";
545 
546  for(const QString& snip : line.split(QRegExp("\\s|\\t"), QString::SkipEmptyParts))
547  {
548  switch (countInLine)
549  {
550  case 0:
551  red = snip.toInt();
552  break;
553  case 1:
554  green = snip.toInt();
555  break;
556  case 2:
557  blue = snip.toInt();
558  break;
559  default:
560  name += snip + " ";
561  }
562  countInLine++;
563  }
564 
565  // trim additional spaces
566  name = name.trimmed();
567 
568  // Get values from previous color if necessary
569  if (countInLine < 2) green = prevColor.green();
570  if (countInLine < 3) blue = prevColor.blue();
571 
572  // GIMP assigns colors the name "Untitled" by default now
573  // so in addition to missing names, we also use automatic
574  // naming for this
575  if (name.isEmpty() || name == "Untitled") name = QString();
576 
577  QColor color(red, green, blue);
578  if (color.isValid())
579  {
580  mPalette.append(ColorRef(color, name));
581  prevColor = color;
582  }
583  } while (in.readLineInto(&line));
584 }
585 
586 void Object::importPalettePencil(QFile& file)
587 {
588  QDomDocument doc;
589  doc.setContent(&file);
590 
591  QDomElement docElem = doc.documentElement();
592  QDomNode tag = docElem.firstChild();
593  while (!tag.isNull())
594  {
595  QDomElement e = tag.toElement(); // try to convert the node to an element.
596  if (!e.isNull())
597  {
598  QString name = e.attribute("name");
599  int r = e.attribute("red").toInt();
600  int g = e.attribute("green").toInt();
601  int b = e.attribute("blue").toInt();
602  int a = e.attribute("alpha", "255").toInt();
603  mPalette.append(ColorRef(QColor(r, g, b, a), name));
604  }
605  tag = tag.nextSibling();
606  }
607 }
608 
609 void Object::openPalette(QString filePath)
610 {
611  if (!QFile::exists(filePath))
612  {
613  return;
614  }
615 
616  mPalette.clear();
617  importPalette(filePath);
618 }
619 
620 /*
621  * Imports palette, e.g. appends to palette
622 */
623 bool Object::importPalette(QString filePath)
624 {
625  QFile file(filePath);
626 
627  if (!file.open(QFile::ReadOnly))
628  {
629  return false;
630  }
631 
632  if (file.fileName().endsWith(".gpl", Qt::CaseInsensitive))
633  {
634  importPaletteGPL(file);
635  } else {
636  importPalettePencil(file);
637  }
638  file.close();
639  return true;
640 }
641 
642 
643 void Object::loadDefaultPalette()
644 {
645  mPalette.clear();
646  addColor(ColorRef(QColor(Qt::black), QString(tr("Black"))));
647  addColor(ColorRef(QColor(Qt::red), QString(tr("Red"))));
648  addColor(ColorRef(QColor(Qt::darkRed), QString(tr("Dark Red"))));
649  addColor(ColorRef(QColor(255, 128, 0), QString(tr("Orange"))));
650  addColor(ColorRef(QColor(128, 64, 0), QString(tr("Dark Orange"))));
651  addColor(ColorRef(QColor(Qt::yellow), QString(tr("Yellow"))));
652  addColor(ColorRef(QColor(Qt::darkYellow), QString(tr("Dark Yellow"))));
653  addColor(ColorRef(QColor(Qt::green), QString(tr("Green"))));
654  addColor(ColorRef(QColor(Qt::darkGreen), QString(tr("Dark Green"))));
655  addColor(ColorRef(QColor(Qt::cyan), QString(tr("Cyan"))));
656  addColor(ColorRef(QColor(Qt::darkCyan), QString(tr("Dark Cyan"))));
657  addColor(ColorRef(QColor(Qt::blue), QString(tr("Blue"))));
658  addColor(ColorRef(QColor(Qt::darkBlue), QString(tr("Dark Blue"))));
659  addColor(ColorRef(QColor(255, 255, 255), QString(tr("White"))));
660  addColor(ColorRef(QColor(220, 220, 229), QString(tr("Very Light Grey"))));
661  addColor(ColorRef(QColor(Qt::lightGray), QString(tr("Light Grey"))));
662  addColor(ColorRef(QColor(Qt::gray), QString(tr("Grey"))));
663  addColor(ColorRef(QColor(Qt::darkGray), QString(tr("Dark Grey"))));
664  addColor(ColorRef(QColor(255, 227, 187), QString(tr("Pale Orange Yellow"))));
665  addColor(ColorRef(QColor(221, 196, 161), QString(tr("Pale Grayish Orange Yellow"))));
666  addColor(ColorRef(QColor(255, 214, 156), QString(tr("Orange Yellow "))));
667  addColor(ColorRef(QColor(207, 174, 127), QString(tr("Grayish Orange Yellow"))));
668  addColor(ColorRef(QColor(255, 198, 116), QString(tr("Light Orange Yellow"))));
669  addColor(ColorRef(QColor(227, 177, 105), QString(tr("Light Grayish Orange Yellow")) ));
670 }
671 
672 void Object::paintImage(QPainter& painter,int frameNumber,
673  bool background,
674  bool antialiasing) const
675 {
676  updateActiveFrames(frameNumber);
677 
678  painter.setRenderHint(QPainter::Antialiasing, true);
681 
682  // paints the background
683  if (background)
684  {
685  painter.setPen(Qt::NoPen);
686  painter.setBrush(Qt::white);
687  painter.setWorldMatrixEnabled(false);
688  painter.drawRect(QRect(0, 0, painter.device()->width(), painter.device()->height()));
689  painter.setWorldMatrixEnabled(true);
690  }
691 
692  for (int i = 0; i < getLayerCount(); i++)
693  {
694  Layer* layer = getLayer(i);
695  if (layer->visible())
696  {
697  painter.setOpacity(1.0);
698 
699  // paints the bitmap images
700  if (layer->type() == Layer::BITMAP)
701  {
702  LayerBitmap* layerBitmap = static_cast<LayerBitmap*>(layer);
703 
704  BitmapImage* bitmap = layerBitmap->getLastBitmapImageAtFrame(frameNumber);
705  if (bitmap != nullptr)
706  {
707  bitmap->paintImage(painter);
708  }
709 
710  }
711  // paints the vector images
712  if (layer->type() == Layer::VECTOR)
713  {
714  LayerVector* layerVector = static_cast<LayerVector*>(layer);
715  VectorImage* vec = layerVector->getLastVectorImageAtFrame(frameNumber, 0);
716  if (vec != nullptr)
717  {
718  vec->paintImage(painter, false, false, antialiasing);
719  }
720  }
721  }
722  }
723 }
724 
725 QString Object::copyFileToDataFolder(QString strFilePath)
726 {
727  if (!QFile::exists(strFilePath))
728  {
729  qDebug() << "[Object] sound file doesn't exist: " << strFilePath;
730  return "";
731  }
732 
733  QString sNewFileName = "sound_";
734  sNewFileName += QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss_zzz.");
735  sNewFileName += QFileInfo(strFilePath).suffix();
736 
737  QString srcFile = strFilePath;
738  QString destFile = QDir(mDataDirPath).filePath(sNewFileName);
739 
740  if (QFile::exists(destFile))
741  {
742  QFile::remove(destFile);
743  }
744 
745  bool bCopyOK = QFile::copy(srcFile, destFile);
746  if (!bCopyOK)
747  {
748  qDebug() << "[Object] couldn't copy sound file to data folder: " << strFilePath;
749  return "";
750  }
751 
752  return destFile;
753 }
754 
755 bool Object::exportFrames(int frameStart, int frameEnd,
756  const LayerCamera* cameraLayer,
757  QSize exportSize,
758  QString filePath,
759  QString format,
760  bool transparency,
761  bool exportKeyframesOnly,
762  const QString& layerName,
763  bool antialiasing,
764  QProgressDialog* progress = nullptr,
765  int progressMax = 50) const
766 {
767  Q_ASSERT(cameraLayer);
768 
769  QSettings settings(PENCIL2D, PENCIL2D);
770 
771  QString extension = "";
772  QString formatStr = format;
773  if (formatStr == "PNG" || formatStr == "png")
774  {
775  format = "PNG";
776  extension = ".png";
777  }
778  if (formatStr == "JPG" || formatStr == "jpg" || formatStr == "JPEG" || formatStr == "jpeg")
779  {
780  format = "JPG";
781  extension = ".jpg";
782  transparency = false; // JPG doesn't support transparency so we have to include the background
783  }
784  if (formatStr == "TIFF" || formatStr == "tiff" || formatStr == "TIF" || formatStr == "tif")
785  {
786  format = "TIFF";
787  extension = ".tiff";
788  }
789  if (formatStr == "BMP" || formatStr == "bmp")
790  {
791  format = "BMP";
792  extension = ".bmp";
793  transparency = false;
794  }
795  if (filePath.endsWith(extension, Qt::CaseInsensitive))
796  {
797  filePath.chop(extension.size());
798  }
799 
800  qDebug() << "Exporting frames from "
801  << frameStart << "to"
802  << frameEnd
803  << "at size " << exportSize;
804 
805  for (int currentFrame = frameStart; currentFrame <= frameEnd; currentFrame++)
806  {
807  if (progress != nullptr)
808  {
809  int totalFramesToExport = (frameEnd - frameStart) + 1;
810  if (totalFramesToExport != 0) // Avoid dividing by zero.
811  {
812  progress->setValue((currentFrame - frameStart + 1) * progressMax / totalFramesToExport);
813  QApplication::processEvents(); // Required to make progress bar update on-screen.
814  }
815 
816  if (progress->wasCanceled())
817  {
818  break;
819  }
820  }
821 
822  QTransform view = cameraLayer->getViewAtFrame(currentFrame);
823  QSize camSize = cameraLayer->getViewSize();
824 
825  QString frameNumberString = QString::number(currentFrame);
826  while (frameNumberString.length() < 4)
827  {
828  frameNumberString.prepend("0");
829  }
830  QString sFileName = filePath + frameNumberString + extension;
831  Layer* layer = findLayerByName(layerName);
832  if (exportKeyframesOnly)
833  {
834  if (layer->keyExists(currentFrame))
835  exportIm(currentFrame, view, camSize, exportSize, sFileName, format, antialiasing, transparency);
836  }
837  else
838  {
839  exportIm(currentFrame, view, camSize, exportSize, sFileName, format, antialiasing, transparency);
840  }
841  }
842 
843  return true;
844 }
845 
846 bool Object::exportX(int frameStart, int frameEnd, QTransform view, QSize exportSize, QString filePath, bool antialiasing)
847 {
848  QSettings settings(PENCIL2D, PENCIL2D);
849 
850  int page;
851  page = 0;
852  for (int j = frameStart; j <= frameEnd; j = j + 15)
853  {
855  QPainter xPainter(&xImg);
856  xPainter.fillRect(0, 0, 2300, 3400, Qt::white);
857  int y = j - 1;
858  for (int i = j; i < 15 + page * 15 && i <= frameEnd; i++)
859  {
860  QRect source = QRect(QPoint(0, 0), exportSize);
861  QRect target = QRect(QPoint((y % 3) * 800 + 30, (y / 3) * 680 + 50 - page * 3400), QSize(640, 480));
862  QTransform thumbView = view * RectMapTransform(source, target);
863  xPainter.setWorldTransform(thumbView);
864  xPainter.setClipRegion(thumbView.inverted().map(QRegion(target)));
865  paintImage(xPainter, i, false, antialiasing);
866  xPainter.resetTransform();
867  xPainter.setClipping(false);
868  xPainter.setPen(Qt::black);
869  xPainter.drawRect(target);
870  xPainter.drawText(QPoint((y % 3) * 800 + 35, (y / 3) * 680 + 65 - page * 3400), QString::number(i));
871  y++;
872  }
873 
874  if (filePath.endsWith(".jpg", Qt::CaseInsensitive))
875  {
876  filePath.chop(4);
877  }
878  if (!xImg.save(filePath + QString::number(page) + ".jpg", "JPG", 60))
879  {
880  return false;
881  }
882  page++;
883  }
884 
885  return true;
886 }
887 
888 bool Object::exportIm(int frame, QTransform view, QSize cameraSize, QSize exportSize, QString filePath, QString format, bool antialiasing, bool transparency) const
889 {
890  QImage imageToExport(exportSize, QImage::Format_ARGB32_Premultiplied);
891 
892  QColor bgColor = Qt::white;
893  if (transparency)
894  bgColor.setAlpha(0);
895  imageToExport.fill(bgColor);
896 
897  QTransform centralizeCamera;
898  centralizeCamera.translate(cameraSize.width() / 2, cameraSize.height() / 2);
899 
900  QPainter painter(&imageToExport);
901  painter.setWorldTransform(view * centralizeCamera);
902  painter.setWindow(QRect(0, 0, cameraSize.width(), cameraSize.height()));
903 
904  paintImage(painter, frame, false, antialiasing);
905 
906  return imageToExport.save(filePath, format.toStdString().c_str());
907 }
908 
909 int Object::getLayerCount() const
910 {
911  return mLayers.size();
912 }
913 
914 ObjectData* Object::data() const
915 {
916  Q_ASSERT(mData != nullptr);
917  return mData.get();
918 }
919 
920 void Object::setData(ObjectData* d)
921 {
922  Q_ASSERT(d != nullptr);
923  mData.reset(d);
924 }
925 
926 int Object::totalKeyFrameCount() const
927 {
928  int sum = 0;
929  for (const Layer* layer : mLayers)
930  {
931  sum += layer->keyFrameCount();
932  }
933  return sum;
934 }
935 
936 void Object::updateActiveFrames(int frame) const
937 {
938  int beginFrame = std::max(frame - 3, 1);
939  int endFrame = frame + 4;
940  for (int i = 0; i < getLayerCount(); ++i)
941  {
942  Layer* layer = getLayer(i);
943  for (int k = beginFrame; k < endFrame; ++k)
944  {
945  KeyFrame* key = layer->getKeyFrameAt(k);
946  mActiveFramePool->put(key);
947  }
948  }
949 }
950 
951 void Object::setActiveFramePoolSize(int sizeInMB)
952 {
953  // convert MB to Byte
954  mActiveFramePool->resize(qint64(sizeInMB) * 1024 * 1024);
955 }
std::string toStdString() const const
QString toString(Qt::DateFormat format) const const
void setOpacity(qreal opacity)
void paintImage(QPainter &painter, bool simplified, bool showThinCurves, bool antialiasing)
VectorImage::paintImage.
int width() const const
void setCompositionMode(QPainter::CompositionMode mode)
void setRenderHint(QPainter::RenderHint hint, bool on)
Format_ARGB32_Premultiplied
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const const
bool remove()
QPoint map(const QPoint &point) const const
QString & prepend(QChar ch)
const T & at(int i) const const
virtual QString fileName() const const override
int size() const const
void removeAt(int i)
QString filePath(const QString &fileName) const const
void setAlpha(int alpha)
void move(int from, int to)
QDomElement documentElement() const const
bool exists() const const
void chop(int n)
QString tr(const char *sourceText, const char *disambiguation, int n)
bool copy(const QString &newName)
QTransform inverted(bool *invertible) const const
int size() const const
QDomNode nextSibling() const const
int width() const const
QDomElement toElement() const const
void setWindow(const QRect &rectangle)
void drawRect(const QRectF &rectangle)
QColor toRgb() const const
QTransform & translate(qreal dx, qreal dy)
QString number(int n, int base)
void processEvents(QEventLoop::ProcessEventsFlags flags)
QString tempPath()
int red() const const
void setPen(const QColor &color)
void setWorldTransform(const QTransform &matrix, bool combine)
void setAttribute(const QString &name, const QString &value)
CaseInsensitive
int toInt(bool *ok, int base) const const
bool isEmpty() const const
QString trimmed() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QPaintDevice * device() const const
Definition: layer.h:39
void setBrush(const QBrush &brush)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
virtual bool open(QIODevice::OpenMode mode) override
int alpha() const const
int green() const const
CompositionMode_SourceOver
void setParent(QObject *parent)
bool isNull() const const
int blue() const const
QDateTime currentDateTime()
void save(QTextStream &stream, int indent, QDomNode::EncodingPolicy encodingPolicy) const const
QDomNode firstChild() const const
QString suffix() const const
void insert(int i, const T &value)
virtual void close() override
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void setWorldMatrixEnabled(bool enable)
int height() const const
int length() const const
QString tagName() const const
ActiveFramePool implemented a LRU cache to keep tracking the most recent accessed key frames A key fr...
int height() const const
QDomElement createElement(const QString &tagName)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString baseName() const const
bool isValid() const const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)