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