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 "layerbitmap.h"
18
19 #include <QDebug>
20 #include <QDir>
21 #include <QFile>
22 #include "keyframe.h"
23 #include "bitmapimage.h"
24
25
26
27
LayerBitmap(Object * object)28 LayerBitmap::LayerBitmap(Object* object) : Layer(object, Layer::BITMAP)
29 {
30 setName(tr("Bitmap Layer"));
31 }
32
~LayerBitmap()33 LayerBitmap::~LayerBitmap()
34 {
35 }
36
getBitmapImageAtFrame(int frameNumber)37 BitmapImage* LayerBitmap::getBitmapImageAtFrame(int frameNumber)
38 {
39 Q_ASSERT(frameNumber >= 1);
40 return static_cast<BitmapImage*>(getKeyFrameAt(frameNumber));
41 }
42
getLastBitmapImageAtFrame(int frameNumber,int increment)43 BitmapImage* LayerBitmap::getLastBitmapImageAtFrame(int frameNumber, int increment)
44 {
45 Q_ASSERT(frameNumber >= 1);
46 return static_cast<BitmapImage*>(getLastKeyFrameAtPosition(frameNumber + increment));
47 }
48
loadImageAtFrame(QString path,QPoint topLeft,int frameNumber)49 void LayerBitmap::loadImageAtFrame(QString path, QPoint topLeft, int frameNumber)
50 {
51 BitmapImage* pKeyFrame = new BitmapImage(topLeft, path);
52 pKeyFrame->enableAutoCrop(true);
53 pKeyFrame->setPos(frameNumber);
54 loadKey(pKeyFrame);
55 }
56
saveKeyFrameFile(KeyFrame * keyframe,QString path)57 Status LayerBitmap::saveKeyFrameFile(KeyFrame* keyframe, QString path)
58 {
59 QString strFilePath = filePath(keyframe, QDir(path));
60
61 BitmapImage* bitmapImage = static_cast<BitmapImage*>(keyframe);
62
63 bool needSave = needSaveFrame(keyframe, strFilePath);
64 if (!needSave)
65 {
66 return Status::SAFE;
67 }
68
69 bitmapImage->setFileName(strFilePath);
70
71 Status st = bitmapImage->writeFile(strFilePath);
72 if (!st.ok())
73 {
74 bitmapImage->setFileName("");
75
76 DebugDetails dd;
77 dd << "LayerBitmap::saveKeyFrame";
78 dd << QString(" KeyFrame.pos() = %1").arg(keyframe->pos());
79 dd << QString(" strFilePath = %1").arg(strFilePath);
80 dd << QString("BitmapImage could not be saved");
81 dd.collect(st.details());
82 return Status(Status::FAIL, dd);
83 }
84
85 bitmapImage->setModified(false);
86 return Status::OK;
87 }
88
createKeyFrame(int position,Object *)89 KeyFrame* LayerBitmap::createKeyFrame(int position, Object*)
90 {
91 BitmapImage* b = new BitmapImage;
92 b->setPos(position);
93 b->enableAutoCrop(true);
94 return b;
95 }
96
presave(const QString & sDataFolder)97 Status LayerBitmap::presave(const QString& sDataFolder)
98 {
99 QDir dataFolder(sDataFolder);
100 // Handles keys that have been moved but not modified
101 std::vector<BitmapImage*> movedOnlyBitmaps;
102 foreachKeyFrame([&movedOnlyBitmaps,&dataFolder,this](KeyFrame* key)
103 {
104 auto bitmap = static_cast<BitmapImage*>(key);
105 // (b->fileName() != fileName(b) && !modified => the keyframe has been moved, but users didn't draw on it.
106 if (!bitmap->fileName().isEmpty()
107 && !bitmap->isModified()
108 && bitmap->fileName() != filePath(bitmap, dataFolder))
109 {
110 movedOnlyBitmaps.push_back(bitmap);
111 }
112 });
113
114 for (BitmapImage* b : movedOnlyBitmaps)
115 {
116 // Move to temporary locations first to avoid overwritting anything we shouldn't be
117 // Ex: Frame A moves from 1 -> 2, Frame B moves from 2 -> 3. Make sure A does not overwrite B
118 QString tmpPath = dataFolder.filePath(QString::asprintf("t_%03d.%03d.png", id(), b->pos()));
119 if (QFileInfo(b->fileName()).dir() != dataFolder) {
120 // Copy instead of move if the data folder itself has changed
121 QFile::copy(b->fileName(), tmpPath);
122 }
123 else {
124 QFile::rename(b->fileName(), tmpPath);
125 }
126 b->setFileName(tmpPath);
127 }
128
129 for (BitmapImage* b : movedOnlyBitmaps)
130 {
131 QString dest = filePath(b, dataFolder);
132 QFile::remove(dest);
133
134 QFile::rename(b->fileName(), dest);
135 b->setFileName(dest);
136 }
137
138 return Status::OK;
139 }
140
filePath(KeyFrame * key,const QDir & dataFolder) const141 QString LayerBitmap::filePath(KeyFrame* key, const QDir& dataFolder) const
142 {
143 return dataFolder.filePath(fileName(key));
144 }
145
fileName(KeyFrame * key) const146 QString LayerBitmap::fileName(KeyFrame* key) const
147 {
148 return QString::asprintf("%03d.%03d.png", id(), key->pos());
149 }
150
needSaveFrame(KeyFrame * key,const QString & savePath)151 bool LayerBitmap::needSaveFrame(KeyFrame* key, const QString& savePath)
152 {
153 if (key->isModified()) // keyframe was modified
154 return true;
155 if (QFile::exists(savePath) == false) // hasn't been saved before
156 return true;
157 if (key->fileName().isEmpty())
158 return true;
159 return false;
160 }
161
createDomElement(QDomDocument & doc) const162 QDomElement LayerBitmap::createDomElement(QDomDocument& doc) const
163 {
164 QDomElement layerElem = createBaseDomElement(doc);
165
166 foreachKeyFrame([&](KeyFrame* pKeyFrame)
167 {
168 BitmapImage* pImg = static_cast<BitmapImage*>(pKeyFrame);
169
170 QDomElement imageTag = doc.createElement("image");
171 imageTag.setAttribute("frame", pKeyFrame->pos());
172 imageTag.setAttribute("src", fileName(pKeyFrame));
173 imageTag.setAttribute("topLeftX", pImg->topLeft().x());
174 imageTag.setAttribute("topLeftY", pImg->topLeft().y());
175 layerElem.appendChild(imageTag);
176
177 Q_ASSERT(QFileInfo(pKeyFrame->fileName()).fileName() == fileName(pKeyFrame));
178 });
179
180 return layerElem;
181 }
182
loadDomElement(const QDomElement & element,QString dataDirPath,ProgressCallback progressStep)183 void LayerBitmap::loadDomElement(const QDomElement& element, QString dataDirPath, ProgressCallback progressStep)
184 {
185 this->loadBaseDomElement(element);
186
187 QDomNode imageTag = element.firstChild();
188 while (!imageTag.isNull())
189 {
190 QDomElement imageElement = imageTag.toElement();
191 if (!imageElement.isNull())
192 {
193 if (imageElement.tagName() == "image")
194 {
195 QString path = dataDirPath + "/" + imageElement.attribute("src"); // the file is supposed to be in the data directory
196 QFileInfo fi(path);
197 if (!fi.exists()) path = imageElement.attribute("src");
198 int position = imageElement.attribute("frame").toInt();
199 int x = imageElement.attribute("topLeftX").toInt();
200 int y = imageElement.attribute("topLeftY").toInt();
201 loadImageAtFrame(path, QPoint(x, y), position);
202
203 progressStep();
204 }
205 }
206 imageTag = imageTag.nextSibling();
207 }
208 }
209