19#include <QDomDocument>
21#include <QProgressDialog>
22#include <QApplication>
28#include <QImageWriter>
29#include <QRegularExpression>
32#include "layerbitmap.h"
33#include "layervector.h"
34#include "layersound.h"
35#include "layercamera.h"
38#include "bitmapimage.h"
39#include "vectorimage.h"
40#include "fileformat.h"
41#include "activeframepool.h"
51 mActiveFramePool->clear();
53 for (
Layer* layer : mLayers)
72 for (
Layer* layer : mLayers)
74 QDomElement layerTag = layer->createDomElement(doc);
80bool Object::loadXML(
const QDomElement& docElem, ProgressCallback progressForward)
87 const QString dataDirPath = mDataDirPath;
92 if (element.
tagName() !=
"layer")
107 newLayer =
new LayerSound(getUniqueLayerID());
115 mLayers.append(newLayer);
116 newLayer->loadDomElement(element, dataDirPath, progressForward);
124 mLayers.append(layerBitmap);
134 mLayers.append(layerVector);
144 mLayers.append(layerSound);
154 mLayers.append(layerCamera);
161void Object::createWorkingDir()
166 projectName =
"Default";
171 projectName = fileInfo.completeBaseName();
180 PFF_TMP_DECOMPRESS_EXT,
183 while(dir.exists(strWorkingDir));
185 dir.mkpath(strWorkingDir);
186 mWorkingDirPath = strWorkingDir;
188 QDir dataDir(strWorkingDir + PFF_DATA_DIR);
191 mDataDirPath = dataDir.absolutePath();
194void Object::deleteWorkingDir()
const
196 if (!mWorkingDirPath.
isEmpty())
198 QDir dir(mWorkingDirPath);
199 bool ok = dir.removeRecursively();
204void Object::setWorkingDir(
const QString& path)
207 Q_ASSERT(dir.exists());
208 mWorkingDirPath = path;
211int Object::getMaxLayerID()
214 for (
Layer* iLayer : mLayers)
216 if (iLayer->id() > maxId)
218 maxId = iLayer->id();
224int Object::getUniqueLayerID()
226 return 1 + getMaxLayerID();
229Layer* Object::getLayer(
int i)
const
231 if (i < 0 || i >= getLayerCount())
236 return mLayers.at(i);
239Layer* Object::getLayerBelow(
int i, Layer::LAYER_TYPE type)
const
243 Layer* layerCheck = getLayer(i);
244 Q_ASSERT(layerCheck);
245 if (layerCheck->type() == type)
254Layer* Object::findLayerById(
int layerId)
const
256 for(
Layer* layer : mLayers)
258 if (layer->id() == layerId)
266Layer* Object::findLayerByName(
const QString& strName, Layer::LAYER_TYPE type)
const
268 bool bCheckType = (type != Layer::UNDEFINED);
269 for (
Layer* layer : mLayers)
271 bool isTypeMatch = (bCheckType) ? (type == layer->type()) :
true;
272 if (isTypeMatch && layer->name() == strName)
280Layer* Object::takeLayer(
int layerId)
285 for (
int i = 0; i< mLayers.length(); ++i)
287 Layer* layer = mLayers[i];
288 if (layer->id() == layerId)
295 if (index == -1) {
return nullptr; }
297 Layer* layer = mLayers.takeAt(index);
301bool Object::swapLayers(
int i,
int j)
304 if (!canSwap) {
return false; }
308#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
309 mLayers.swapItemsAt(i, j);
319 if (layerIndexLeft < 0 || layerIndexLeft >= mLayers.size())
324 if (layerIndexRight < 0 || layerIndexRight >= mLayers.size())
329 Layer* firstLayer = mLayers.first();
330 Layer* leftLayer = mLayers.at(layerIndexLeft);
331 Layer* rightLayer = mLayers.at(layerIndexRight);
334 if ((leftLayer->type() == Layer::CAMERA ||
335 rightLayer->type() == Layer::CAMERA) &&
336 (firstLayer == leftLayer || firstLayer == rightLayer)) {
349 if (mLayers.at(index) ==
nullptr)
357void Object::deleteLayer(
int i)
359 if (i > -1 && i < mLayers.size())
361 delete mLayers.takeAt(i);
365void Object::deleteLayer(
Layer* layer)
367 auto it = std::find(mLayers.begin(), mLayers.end(), layer);
369 if (it != mLayers.end())
376bool Object::addLayer(
Layer* layer)
378 if (layer ==
nullptr || mLayers.contains(layer))
382 layer->setId(getUniqueLayerID());
383 mLayers.append(layer);
387ColorRef Object::getColor(
int index)
const
390 if (index > -1 && index < mPalette.
size())
392 result = mPalette.
at(index);
397void Object::setColor(
int index,
const QColor& newColor)
399 Q_ASSERT(index >= 0);
401 mPalette[index].color = newColor;
404void Object::setColorRef(
int index,
const ColorRef& newColorRef)
406 mPalette[index] = newColorRef;
409void Object::movePaletteColor(
int start,
int end)
411 mPalette.
move(start, end);
414void Object::moveVectorColor(
int start,
int end)
416 for (
Layer* layer : mLayers)
418 if (layer->type() == Layer::VECTOR)
420 static_cast<LayerVector*
>(layer)->moveColor(start, end);
425void Object::addColorAtIndex(
int index,
const ColorRef& newColor)
427 mPalette.
insert(index, newColor);
430bool Object::isColorInUse(
int index)
const
432 for (
Layer* layer : mLayers)
434 if (layer->type() == Layer::VECTOR)
438 if (layerVector->usesColor(index))
447void Object::removeColor(
int index)
449 for (
Layer* layer : mLayers)
451 if (layer->type() == Layer::VECTOR)
454 layerVector->removeColor(index);
463void Object::renameColor(
int i,
const QString& text)
465 mPalette[i].name = text;
471 bool ok = exportPalette(fullPath);
477void Object::exportPaletteGPL(
QFile& file)
const
482 out <<
"GIMP Palette" <<
"\n";
483 out <<
"Name: " << fileName <<
"\n";
486 for (
const ColorRef& ref : mPalette)
490 out <<
" " << ref.name <<
"\n";
494void Object::exportPalettePencil(
QFile& file)
const
501 for (
const ColorRef& ref : mPalette)
512 doc.
save(out, indentSize);
515bool Object::exportPalette(
const QString& filePath)
const
517 QFile file(filePath);
520 qDebug(
"Error: cannot export palette");
525 exportPaletteGPL(file);
527 exportPalettePencil(file);
540void Object::importPaletteGPL(
QFile& file)
547 in.readLineInto(&line);
550 in.readLineInto(&line);
555 in.readLineInto(&line);
560 in.readLineInto(&line);
579#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
591 green = snip.toInt();
606 if (countInLine < 2)
green = prevColor.green();
607 if (countInLine < 3)
blue = prevColor.blue();
614 QColor color(red, green, blue);
617 mPalette.append(
ColorRef(color, name));
620 }
while (in.readLineInto(&line));
623void Object::importPalettePencil(
QFile& file)
646void Object::openPalette(
const QString& filePath)
648 if (!QFile::exists(filePath))
654 importPalette(filePath);
660bool Object::importPalette(
const QString& filePath)
662 QFile file(filePath);
671 importPaletteGPL(file);
673 importPalettePencil(file);
679void Object::loadDefaultPalette()
700 addColor(
ColorRef(
QColor(255, 227, 187), tr(
"Pale Orange Yellow")));
701 addColor(
ColorRef(
QColor(221, 196, 161), tr(
"Pale Grayish Orange Yellow")));
703 addColor(
ColorRef(
QColor(207, 174, 127), tr(
"Grayish Orange Yellow")));
704 addColor(
ColorRef(
QColor(255, 198, 116), tr(
"Light Orange Yellow")));
705 addColor(
ColorRef(
QColor(227, 177, 105), tr(
"Light Grayish Orange Yellow")));
708void Object::paintImage(
QPainter& painter,
int frameNumber,
710 bool antialiasing)
const
712 updateActiveFrames(frameNumber);
728 for (
Layer* layer : mLayers)
730 if (!layer->visible())
737 if (layer->type() == Layer::BITMAP)
741 BitmapImage* bitmap = layerBitmap->getLastBitmapImageAtFrame(frameNumber);
745 bitmap->paintImage(painter);
750 if (layer->type() == Layer::VECTOR)
753 VectorImage* vec = layerVector->getLastVectorImageAtFrame(frameNumber, 0);
757 vec->
paintImage(painter, *
this,
false,
false, antialiasing);
765 if (!QFile::exists(strFilePath))
767 qDebug() <<
"[Object] sound file doesn't exist: " << strFilePath;
771 QString sNewFileName =
"sound_";
777 if (QFile::exists(destFile))
779 QFile::remove(destFile);
782 bool bCopyOK = QFile::copy(strFilePath, destFile);
785 qDebug() <<
"[Object] couldn't copy sound file to data folder: " << strFilePath;
792Status Object::exportFrames(
int frameStart,
int frameEnd,
798 bool exportKeyframesOnly,
802 int progressMax = 50)
const
804 Q_ASSERT(cameraLayer);
808 if (formatStr ==
"PNG" || formatStr ==
"png")
813 if (formatStr ==
"JPG" || formatStr ==
"jpg" || formatStr ==
"JPEG" || formatStr ==
"jpeg")
817 transparency =
false;
819 if (formatStr ==
"TIFF" || formatStr ==
"tiff" || formatStr ==
"TIF" || formatStr ==
"tif")
824 if (formatStr ==
"BMP" || formatStr ==
"bmp")
828 transparency =
false;
830 if (formatStr ==
"WEBP" || formatStr ==
"webp") {
839 qDebug() <<
"Exporting frames from "
840 << frameStart <<
"to"
842 <<
"at size " << exportSize;
845 dd <<
"\n[Export frames diagnostics]\n";
848 for (
int currentFrame = frameStart; currentFrame <= frameEnd; currentFrame++)
850 if (progress !=
nullptr)
852 int totalFramesToExport = (frameEnd - frameStart) + 1;
853 if (totalFramesToExport != 0)
855 progress->setValue((currentFrame - frameStart + 1) * progressMax / totalFramesToExport);
859 if (progress->wasCanceled())
865 QTransform view = cameraLayer->getViewAtFrame(currentFrame);
866 QSize camSize = cameraLayer->getViewSize();
869 while (frameNumberString.
length() < 4)
871 frameNumberString.
prepend(
"0");
873 QString sFileName = filePath + frameNumberString + extension;
874 Layer* layer = findLayerByName(layerName);
876 if (exportKeyframesOnly)
878 if (layer->keyExists(currentFrame))
880 st = exportIm(currentFrame, view, camSize, exportSize, sFileName, format, antialiasing, transparency);
885 st = exportIm(currentFrame, view, camSize, exportSize, sFileName, format, antialiasing, transparency);
891 dd.collect(st.details());
897 dd <<
"\nError: Failed to export one or more frames";
898 return Status(Status::FAIL, dd);
911 imageToExport.fill(bgColor);
920 paintImage(painter, frame,
false, antialiasing);
923 bool b = writer.write(imageToExport);
928 dd <<
"Object::exportIm";
930 dd <<
QString(
" Error: %1 (code %2)").
arg(writer.errorString()).
arg(
static_cast<int>(writer.error()));
931 return Status(Status::FAIL, dd);
935int Object::getLayerCount()
const
937 return mLayers.size();
945int Object::totalKeyFrameCount()
const
948 for (
const Layer* layer : mLayers)
950 sum += layer->keyFrameCount();
955void Object::updateActiveFrames(
int frame)
const
957 const int beginFrame = std::max(frame - 3, 1);
958 const int endFrame = frame + 4;
960 const int minFrameCount = getLayerCount() * (endFrame - beginFrame);
961 mActiveFramePool->setMinFrameCount(minFrameCount);
963 for (
Layer* layer : mLayers)
965 if (layer->visible())
967 for (
int k = beginFrame; k < endFrame; ++k)
969 KeyFrame* key = layer->getKeyFrameAt(k);
970 mActiveFramePool->put(key);
976void Object::setActiveFramePoolSize(
int sizeInMB)
979 mActiveFramePool->resize(qint64(sizeInMB) * 1024 * 1024);
ActiveFramePool implemented a LRU cache to keep tracking the most recent accessed key frames A key fr...
bool addNewKeyFrameAt(int position)
Creates a new keyframe at the given position, unless one already exists.
bool canDeleteLayer(int index) const
Allows you to check whether the layer at the given index can be deleted.
bool canSwapLayers(int layerIndexLeft, int layerIndexRight) const
Allows you to check whether two layers can be swappped, before doing the actual operation.
void paintImage(QPainter &painter, const Object &object, bool simplified, bool showThinCurves, bool antialiasing)
VectorImage::paintImage.
QColor toRgb() const const
void processEvents(QEventLoop::ProcessEventsFlags flags)
QDateTime currentDateTime()
QString toString(Qt::DateFormat format) const const
QString filePath(const QString &fileName) const const
QDomElement createElement(const QString &tagName)
QDomElement documentElement() const const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QString attribute(const QString &name, const QString &defValue) const const
void setAttribute(const QString &name, const QString &value)
QString tagName() const const
QDomNode appendChild(const QDomNode &newChild)
QDomNode firstChild() const const
bool isNull() const const
QDomNode nextSibling() const const
void save(QTextStream &stream, int indent, QDomNode::EncodingPolicy encodingPolicy) const const
QDomElement toElement() const const
virtual QString fileName() const const
QString baseName() const const
QString suffix() const const
virtual bool open(QIODevice::OpenMode mode)
const T & at(int i) const const
void insert(int i, const T &value)
void move(int from, int to)
CompositionMode_SourceOver
QPaintDevice * device() const const
void drawRect(const QRectF &rectangle)
void setBrush(const QBrush &brush)
void setCompositionMode(QPainter::CompositionMode mode)
void setOpacity(qreal opacity)
void setPen(const QColor &color)
void setRenderHint(QPainter::RenderHint hint, bool on)
void setWindow(const QRect &rectangle)
void setWorldMatrixEnabled(bool enable)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(int n, int base)
QString & prepend(QChar ch)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
int toStdString() const const
QString trimmed() const const