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("&nbsp;&nbsp;KeyFrame.pos() = %1").arg(keyframe->pos());
97 dd.collect(st.details());
98 return Status(Status::FAIL, dd);
99 }
100
101 bitmapImage->setModified(false);
102 return Status::OK;
103}
104
105KeyFrame* LayerBitmap::createKeyFrame(int position)
106{
107 BitmapImage* b = new BitmapImage;
108 b->setPos(position);
109 b->enableAutoCrop(true);
110 return b;
111}
112
113Status LayerBitmap::presave(const QString& sDataFolder)
114{
115 QDir dataFolder(sDataFolder);
116 // Handles keys that have been moved but not modified
117 std::vector<BitmapImage*> movedOnlyBitmaps;
118 foreachKeyFrame([&movedOnlyBitmaps,&dataFolder,this](KeyFrame* key)
119 {
120 auto bitmap = static_cast<BitmapImage*>(key);
121 // (b->fileName() != fileName(b) && !modified => the keyframe has been moved, but users didn't draw on it.
122 if (!bitmap->fileName().isEmpty()
123 && !bitmap->isModified()
124 && bitmap->fileName() != filePath(bitmap, dataFolder))
125 {
126 movedOnlyBitmaps.push_back(bitmap);
127 }
128 });
129
130 for (BitmapImage* b : movedOnlyBitmaps)
131 {
132 // Move to temporary locations first to avoid overwritting anything we shouldn't be
133 // Ex: Frame A moves from 1 -> 2, Frame B moves from 2 -> 3. Make sure A does not overwrite B
134 QString tmpPath = dataFolder.filePath(QString::asprintf("t_%03d.%03d.png", id(), b->pos()));
135 if (QFileInfo(b->fileName()).dir() != dataFolder) {
136 // Copy instead of move if the data folder itself has changed
137 QFile::copy(b->fileName(), tmpPath);
138 }
139 else {
140 QFile::rename(b->fileName(), tmpPath);
141 }
142 b->setFileName(tmpPath);
143 }
144
145 for (BitmapImage* b : movedOnlyBitmaps)
146 {
147 QString dest = filePath(b, dataFolder);
148 QFile::remove(dest);
149
150 QFile::rename(b->fileName(), dest);
151 b->setFileName(dest);
152 }
153
154 return Status::OK;
155}
156
157QString LayerBitmap::filePath(KeyFrame* key, const QDir& dataFolder) const
158{
159 return dataFolder.filePath(fileName(key));
160}
161
162QString LayerBitmap::fileName(KeyFrame* key) const
163{
164 return QString::asprintf("%03d.%03d.png", id(), key->pos());
165}
166
167bool LayerBitmap::needSaveFrame(KeyFrame* key, const QString& savePath)
168{
169 if (key->isModified()) // keyframe was modified
170 return true;
171 if (QFile::exists(savePath) == false) // hasn't been saved before
172 return true;
173 if (key->fileName().isEmpty())
174 return true;
175 return false;
176}
177
178QDomElement LayerBitmap::createDomElement(QDomDocument& doc) const
179{
180 QDomElement layerElem = createBaseDomElement(doc);
181
182 foreachKeyFrame([&](KeyFrame* pKeyFrame)
183 {
184 BitmapImage* pImg = static_cast<BitmapImage*>(pKeyFrame);
185
186 QDomElement imageTag = doc.createElement("image");
187 imageTag.setAttribute("frame", pKeyFrame->pos());
188 imageTag.setAttribute("src", fileName(pKeyFrame));
189 imageTag.setAttribute("topLeftX", pImg->topLeft().x());
190 imageTag.setAttribute("topLeftY", pImg->topLeft().y());
191 imageTag.setAttribute("opacity", pImg->getOpacity());
192 layerElem.appendChild(imageTag);
193
194 if (!pKeyFrame->fileName().isEmpty()) {
195 Q_ASSERT(QFileInfo(pKeyFrame->fileName()).fileName() == fileName(pKeyFrame));
196 }
197 });
198
199 return layerElem;
200}
201
202void LayerBitmap::loadDomElement(const QDomElement& element, QString dataDirPath, ProgressCallback progressStep)
203{
204 this->loadBaseDomElement(element);
205
206 QDomNode imageTag = element.firstChild();
207 while (!imageTag.isNull())
208 {
209 QDomElement imageElement = imageTag.toElement();
210 if (!imageElement.isNull() && imageElement.tagName() == "image")
211 {
212 QString path = validateDataPath(imageElement.attribute("src"), dataDirPath);
213 if (!path.isEmpty())
214 {
215 int position = imageElement.attribute("frame").toInt();
216 int x = imageElement.attribute("topLeftX").toInt();
217 int y = imageElement.attribute("topLeftY").toInt();
218 qreal opacity = imageElement.attribute("opacity", "1.0").toDouble();
219 loadImageAtFrame(path, QPoint(x, y), position, opacity);
220 }
221
222 progressStep();
223 }
224 imageTag = imageTag.nextSibling();
225 }
226}
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
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 Fri Dec 19 2025 07:54:21 for Pencil2D by doxygen 1.9.6 based on revision 7fd8cd9e03f2d31750e199ecec202e5c0f30e532