All Classes Namespaces Functions Variables Enumerations Properties Pages
layerbitmap.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 "layerbitmap.h"
18 
19 #include <QDebug>
20 #include <QDir>
21 #include <QFile>
22 #include "keyframe.h"
23 #include "bitmapimage.h"
24 
25 
26 
27 
28 LayerBitmap::LayerBitmap(Object* object) : Layer(object, Layer::BITMAP)
29 {
30  setName(tr("Bitmap Layer"));
31 }
32 
33 LayerBitmap::~LayerBitmap()
34 {
35 }
36 
37 BitmapImage* LayerBitmap::getBitmapImageAtFrame(int frameNumber)
38 {
39  Q_ASSERT(frameNumber >= 1);
40  return static_cast<BitmapImage*>(getKeyFrameAt(frameNumber));
41 }
42 
43 BitmapImage* LayerBitmap::getLastBitmapImageAtFrame(int frameNumber, int increment)
44 {
45  Q_ASSERT(frameNumber >= 1);
46  return static_cast<BitmapImage*>(getLastKeyFrameAtPosition(frameNumber + increment));
47 }
48 
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 
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 
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 
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 
141 QString LayerBitmap::filePath(KeyFrame* key, const QDir& dataFolder) const
142 {
143  return dataFolder.filePath(fileName(key));
144 }
145 
146 QString LayerBitmap::fileName(KeyFrame* key) const
147 {
148  return QString::asprintf("%03d.%03d.png", id(), key->pos());
149 }
150 
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 
162 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 
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 }
QString asprintf(const char *cformat,...)
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const const
bool remove()
bool rename(const QString &newName)
QString filePath(const QString &fileName) const const
bool exists() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
bool copy(const QString &newName)
int x() const const
int y() const const
QDomNode nextSibling() const const
QDomElement toElement() const const
void setAttribute(const QString &name, const QString &value)
int toInt(bool *ok, int base) const const
bool isEmpty() const const
Definition: layer.h:39
bool isNull() const const
QDomNode firstChild() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
Definition: object.h:54
QString tagName() const const
QDomElement createElement(const QString &tagName)