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