Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • structure
layerbitmap.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU 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#include "util/util.h"
25
26LayerBitmap::LayerBitmap(int id) : Layer(id, Layer::BITMAP)
27{
28 setName(tr("Bitmap Layer"));
29}
30
31LayerBitmap::~LayerBitmap()
32{
33}
34
35BitmapImage* LayerBitmap::getBitmapImageAtFrame(int frameNumber)
36{
37 Q_ASSERT(frameNumber >= 1);
38 return static_cast<BitmapImage*>(getKeyFrameAt(frameNumber));
39}
40
41BitmapImage* LayerBitmap::getLastBitmapImageAtFrame(int frameNumber, int increment)
42{
43 Q_ASSERT(frameNumber >= 1);
44 return static_cast<BitmapImage*>(getLastKeyFrameAtPosition(frameNumber + increment));
45}
46
47void LayerBitmap::replaceKeyFrame(const KeyFrame* bitmapImage)
48{
49 *getBitmapImageAtFrame(bitmapImage->pos()) = *static_cast<const BitmapImage*>(bitmapImage);
50}
51
52void LayerBitmap::repositionFrame(QPoint point, int frame)
53{
54 BitmapImage* image = getBitmapImageAtFrame(frame);
55 Q_ASSERT(image);
56 image->moveTopLeft(point);
57}
58
59QRect LayerBitmap::getFrameBounds(int frame)
60{
61 BitmapImage* image = getBitmapImageAtFrame(frame);
62 Q_ASSERT(image);
63 return image->bounds();
64}
65
66void LayerBitmap::loadImageAtFrame(QString path, QPoint topLeft, int frameNumber, qreal opacity)
67{
68 BitmapImage* pKeyFrame = new BitmapImage(topLeft, path);
69 pKeyFrame->enableAutoCrop(true);
70 pKeyFrame->setPos(frameNumber);
71 pKeyFrame->setOpacity(opacity);
72 loadKey(pKeyFrame);
73}
74
75Status LayerBitmap::saveKeyFrameFile(KeyFrame* keyframe, QString path)
76{
77 QString strFilePath = filePath(keyframe, QDir(path));
78
79 BitmapImage* bitmapImage = static_cast<BitmapImage*>(keyframe);
80
81 bool needSave = needSaveFrame(keyframe, strFilePath);
82 if (!needSave)
83 {
84 return Status::SAFE;
85 }
86
87 bitmapImage->setFileName(strFilePath);
88
89 Status st = bitmapImage->writeFile(strFilePath);
90 if (!st.ok())
91 {
92 bitmapImage->setFileName("");
93
94 DebugDetails dd;
95 dd << "LayerBitmap::saveKeyFrame";
96 dd << QString(" KeyFrame.pos() = %1").arg(keyframe->pos());
97 dd << QString(" strFilePath = %1").arg(strFilePath);
98 dd << QString("Error: Failed to save BitmapImage");
99 dd.collect(st.details());
100 return Status(Status::FAIL, dd);
101 }
102
103 bitmapImage->setModified(false);
104 return Status::OK;
105}
106
107KeyFrame* LayerBitmap::createKeyFrame(int position)
108{
109 BitmapImage* b = new BitmapImage;
110 b->setPos(position);
111 b->enableAutoCrop(true);
112 return b;
113}
114
115Status LayerBitmap::presave(const QString& sDataFolder)
116{
117 QDir dataFolder(sDataFolder);
118 // Handles keys that have been moved but not modified
119 std::vector<BitmapImage*> movedOnlyBitmaps;
120 foreachKeyFrame([&movedOnlyBitmaps,&dataFolder,this](KeyFrame* key)
121 {
122 auto bitmap = static_cast<BitmapImage*>(key);
123 // (b->fileName() != fileName(b) && !modified => the keyframe has been moved, but users didn't draw on it.
124 if (!bitmap->fileName().isEmpty()
125 && !bitmap->isModified()
126 && bitmap->fileName() != filePath(bitmap, dataFolder))
127 {
128 movedOnlyBitmaps.push_back(bitmap);
129 }
130 });
131
132 for (BitmapImage* b : movedOnlyBitmaps)
133 {
134 // Move to temporary locations first to avoid overwritting anything we shouldn't be
135 // Ex: Frame A moves from 1 -> 2, Frame B moves from 2 -> 3. Make sure A does not overwrite B
136 QString tmpPath = dataFolder.filePath(QString::asprintf("t_%03d.%03d.png", id(), b->pos()));
137 if (QFileInfo(b->fileName()).dir() != dataFolder) {
138 // Copy instead of move if the data folder itself has changed
139 QFile::copy(b->fileName(), tmpPath);
140 }
141 else {
142 QFile::rename(b->fileName(), tmpPath);
143 }
144 b->setFileName(tmpPath);
145 }
146
147 for (BitmapImage* b : movedOnlyBitmaps)
148 {
149 QString dest = filePath(b, dataFolder);
150 QFile::remove(dest);
151
152 QFile::rename(b->fileName(), dest);
153 b->setFileName(dest);
154 }
155
156 return Status::OK;
157}
158
159QString LayerBitmap::filePath(KeyFrame* key, const QDir& dataFolder) const
160{
161 return dataFolder.filePath(fileName(key));
162}
163
164QString LayerBitmap::fileName(KeyFrame* key) const
165{
166 return QString::asprintf("%03d.%03d.png", id(), key->pos());
167}
168
169bool LayerBitmap::needSaveFrame(KeyFrame* key, const QString& savePath)
170{
171 if (key->isModified()) // keyframe was modified
172 return true;
173 if (QFile::exists(savePath) == false) // hasn't been saved before
174 return true;
175 if (key->fileName().isEmpty())
176 return true;
177 return false;
178}
179
180QDomElement LayerBitmap::createDomElement(QDomDocument& doc) const
181{
182 QDomElement layerElem = createBaseDomElement(doc);
183
184 foreachKeyFrame([&](KeyFrame* pKeyFrame)
185 {
186 BitmapImage* pImg = static_cast<BitmapImage*>(pKeyFrame);
187
188 QDomElement imageTag = doc.createElement("image");
189 imageTag.setAttribute("frame", pKeyFrame->pos());
190 imageTag.setAttribute("src", fileName(pKeyFrame));
191 imageTag.setAttribute("topLeftX", pImg->topLeft().x());
192 imageTag.setAttribute("topLeftY", pImg->topLeft().y());
193 imageTag.setAttribute("opacity", pImg->getOpacity());
194 layerElem.appendChild(imageTag);
195
196 if (!pKeyFrame->fileName().isEmpty()) {
197 Q_ASSERT(QFileInfo(pKeyFrame->fileName()).fileName() == fileName(pKeyFrame));
198 }
199 });
200
201 return layerElem;
202}
203
204void LayerBitmap::loadDomElement(const QDomElement& element, QString dataDirPath, ProgressCallback progressStep)
205{
206 this->loadBaseDomElement(element);
207
208 QDomNode imageTag = element.firstChild();
209 while (!imageTag.isNull())
210 {
211 QDomElement imageElement = imageTag.toElement();
212 if (!imageElement.isNull() && imageElement.tagName() == "image")
213 {
214 QString path = validateDataPath(imageElement.attribute("src"), dataDirPath);
215 if (!path.isEmpty())
216 {
217 int position = imageElement.attribute("frame").toInt();
218 int x = imageElement.attribute("topLeftX").toInt();
219 int y = imageElement.attribute("topLeftY").toInt();
220 qreal opacity = imageElement.attribute("opacity", "1.0").toDouble();
221 loadImageAtFrame(path, QPoint(x, y), position, opacity);
222 }
223
224 progressStep();
225 }
226 imageTag = imageTag.nextSibling();
227 }
228}
BitmapImage
Definition: bitmapimage.h:28
DebugDetails
Definition: pencilerror.h:25
KeyFrame
Definition: keyframe.h:30
Layer
Definition: layer.h:33
Status
Definition: pencilerror.h:40
QDir
QDir::filePath
QString filePath(const QString &fileName) const const
QDomDocument
QDomDocument::createElement
QDomElement createElement(const QString &tagName)
QDomElement
QDomElement::attribute
QString attribute(const QString &name, const QString &defValue) const const
QDomElement::setAttribute
void setAttribute(const QString &name, const QString &value)
QDomElement::tagName
QString tagName() const const
QDomNode
QDomNode::appendChild
QDomNode appendChild(const QDomNode &newChild)
QDomNode::firstChild
QDomNode firstChild() const const
QDomNode::isNull
bool isNull() const const
QDomNode::nextSibling
QDomNode nextSibling() const const
QDomNode::toElement
QDomElement toElement() const const
QFile::copy
bool copy(const QString &newName)
QFile::exists
bool exists() const const
QFile::remove
bool remove()
QFile::rename
bool rename(const QString &newName)
QFileInfo
QFileInfo::dir
QDir dir() const const
QFileInfo::fileName
QString fileName() const const
QPoint
QPoint::x
int x() const const
QPoint::y
int y() const const
QRect
QString
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString::asprintf
QString asprintf(const char *cformat,...)
QString::isEmpty
bool isEmpty() const const
QString::toDouble
double toDouble(bool *ok) const const
QString::toInt
int toInt(bool *ok, int base) const const
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39