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