Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAsyncAnimationFramesSaveDialog.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <kis_image.h>
10#include <kis_time_span.h>
11
14
15#include "KisMimeDatabase.h"
16
17#include <QFileInfo>
18#include <QDir>
19#include <QMessageBox>
20#include <QApplication>
21
24 const KisTimeSpan &_range,
25 const QString &baseFilename,
26 int _sequenceNumberingOffset,
27 bool _onlyNeedsUniqueFrames,
28 KisPropertiesConfigurationSP _exportConfiguration)
29 : originalImage(_image),
30 range(_range),
31 onlyNeedsUniqueFrames(_onlyNeedsUniqueFrames),
32 sequenceNumberingOffset(_sequenceNumberingOffset),
33 exportConfiguration(_exportConfiguration)
34 {
35 int baseLength = baseFilename.lastIndexOf(".");
36 if (baseLength > -1) {
37 filenamePrefix = baseFilename.left(baseLength);
38 filenameSuffix = baseFilename.right(baseFilename.length() - baseLength);
39 } else {
40 filenamePrefix = baseFilename;
41 }
42
43 outputMimeType = KisMimeDatabase::mimeTypeForFile(baseFilename, false).toLatin1();
44 }
45
48
51 QByteArray outputMimeType;
53
56};
57
59 const KisTimeSpan &range,
60 const QString &baseFilename,
61 int startNumberingAt,
62 bool onlyNeedsUniqueFrames,
63 KisPropertiesConfigurationSP exportConfiguration)
64 : KisAsyncAnimationRenderDialogBase(i18n("Saving frames..."), originalImage, 0),
65 m_d(new Private(originalImage, range, baseFilename, qMax(startNumberingAt - range.start(), range.start() * -1), onlyNeedsUniqueFrames, exportConfiguration))
66{
67
68
69}
70
74
76{
77 QString dirPath;
78 QString fileWildcard;
79#ifdef Q_OS_ANDROID
80 // QFileInfo can't manage to manipulate content URIs on Android, most
81 // functions either just hand you the entire input or an empty string, so
82 // we have to do the name mangling by hand.
83 {
84 QString wildcardPath = savedFilesMaskWildcard();
85 int lastSlashIndex = wildcardPath.lastIndexOf(QChar('/'));
87 dirPath = wildcardPath.mid(0, lastSlashIndex);
88 fileWildcard = wildcardPath.mid(lastSlashIndex + 1);
89 }
90#else
91 {
92 QFileInfo fileInfo(savedFilesMaskWildcard());
93 dirPath = fileInfo.absolutePath();
94 fileWildcard = fileInfo.fileName();
95 }
96#endif
97 QDir dir(dirPath);
98
99 // On Android, directory existence checks are unreliable and creating
100 // directories is not allowed. We'll just have to take what we're given and
101 // hope that works out for us.
102#ifndef Q_OS_ANDROID
103 if (!dir.exists()) {
104 dir.mkpath(dirPath);
105 }
106 KIS_SAFE_ASSERT_RECOVER_NOOP(dir.exists());
107#endif
108
109 // Check for overwrite. (Batch mode always overwrites.)
110 QStringList preexistingFileNames = dir.entryList({ fileWildcard });
111 if (!preexistingFileNames.isEmpty() && !batchMode()) {
112 QStringList truncatedList = preexistingFileNames;
113
114 while (truncatedList.size() > 3) {
115 truncatedList.takeLast();
116 }
117
118 QString exampleFiles = truncatedList.join(", ");
119 if (truncatedList.size() != preexistingFileNames.size()) {
120 exampleFiles += QString(", ...");
121 }
122
123 QMessageBox::StandardButton result =
124 QMessageBox::warning(qApp->activeWindow(),
125 i18n("Delete old frames?"),
126 i18n("Frames with the same naming "
127 "scheme exist in the destination "
128 "directory. They are going to be "
129 "deleted, continue?\n\n"
130 "Directory: %1\n"
131 "Files: %2",
132 dirPath, exampleFiles),
133 QMessageBox::Yes | QMessageBox::No,
134 QMessageBox::No);
135
136 if (result == QMessageBox::No) {
137 return RenderCancelled;
138 }
139 }
140
141 // Remove any preexisting files.
142 Q_FOREACH (const QString &file, preexistingFileNames) {
143 if (!dir.remove(file)) {
144 if (!batchMode()) {
145 QMessageBox::critical(qApp->activeWindow(),
146 i18n("Failed to delete"),
147 i18n("Failed to delete an old frame file:\n\n"
148 "%1\n\n"
149 "Rendering cancelled.", dir.absoluteFilePath(file)));
150 }
151
152 qWarning() << "WARNING: KisAsyncAnimFramesSaveDialog: Failed to delete old frame file(s):" << dir.absoluteFilePath(file);
153 return RenderFailed;
154 }
155 }
156
157 // Save new frame files.
159
160 // If we cancel rendering or fail rendering process,
161 // lets clean up any files that may have been created
162 // to keep the next render from having artifacts.
163 preexistingFileNames = savedFiles();
164 if (renderingResult != RenderComplete) {
165 Q_FOREACH (const QString &file, preexistingFileNames) {
166 if (dir.exists(file)) {
167 (void)dir.remove(file);
168 }
169 }
170 }
171
172 return renderingResult;
173}
174
176{
177 QList<int> result;
178 for (int frame = m_d->range.start(); frame <= m_d->range.end(); frame++) {
179 KisTimeSpan heldFrameTimeRange = KisTimeSpan::calculateIdenticalFramesRecursive(m_d->originalImage->root(), frame);
180
181 if (!m_d->onlyNeedsUniqueFrames) {
182 // Clamp holds that begin before the rendered range onto it
183 heldFrameTimeRange &= m_d->range;
184 }
185
186 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(heldFrameTimeRange.isValid(), result);
187
188 result.append(heldFrameTimeRange.start());
189
190 if (heldFrameTimeRange.isInfinite()) {
191 break;
192 } else {
193 frame = heldFrameTimeRange.end();
194 }
195 }
196 return result;
197}
198
200{
202 m_d->filenamePrefix,
203 m_d->filenameSuffix,
204 m_d->outputMimeType,
205 m_d->range,
206 m_d->sequenceNumberingOffset,
207 m_d->onlyNeedsUniqueFrames,
208 m_d->exportConfiguration);
209}
210
212{
213 Q_UNUSED(renderer);
214 Q_UNUSED(image);
215 Q_UNUSED(frame);
216}
217
219{
220 return m_d->filenamePrefix + "%04d" + m_d->filenameSuffix;
221}
222
224{
225 return m_d->filenamePrefix + "????" + m_d->filenameSuffix;
226}
227
229{
230 QStringList files;
231
232 for (int i = m_d->range.start(); i <= m_d->range.end(); i++) {
233 const int num = m_d->sequenceNumberingOffset + i;
234 QString name = QString("%1").arg(num, 4, 10, QChar('0'));
235 name = m_d->filenamePrefix + name + m_d->filenameSuffix;
236 files.append(QFileInfo(name).fileName());
237 }
238
239 return files;
240}
241
243{
244 QStringList files;
245
246 const QList<int> frames = calcDirtyFrames();
247
248 Q_FOREACH (int frame, frames) {
249 const int num = m_d->sequenceNumberingOffset + frame;
250 QString name = QString("%1").arg(num, 4, 10, QChar('0'));
251 name = m_d->filenamePrefix + name + m_d->filenameSuffix;
252 files.append(QFileInfo(name).fileName());
253 }
254
255 return files;
256}
257
QList< int > calcDirtyFrames() const override
returns a list of frames that should be regenerated by the dialog
KisAsyncAnimationRendererBase * createRenderer(KisImageSP image) override
create a renderer object linked to image
void initializeRendererForFrame(KisAsyncAnimationRendererBase *renderer, KisImageSP image, int frame) override
Result regenerateRange(KisViewManager *viewManager) override
start generation of frames and (if not in batch mode) show the dialog
KisAsyncAnimationFramesSaveDialog(KisImageSP image, const KisTimeSpan &range, const QString &baseFilename, int startNumberingAt, bool onlyNeedsUniqueFrames, KisPropertiesConfigurationSP exportConfiguration)
KisAsyncAnimationRenderDialogBase is a special class for rendering multiple frames of the image and p...
virtual Result regenerateRange(KisViewManager *viewManager)
start generation of frames and (if not in batch mode) show the dialog
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
int start() const
bool isInfinite() const
static KisTimeSpan calculateIdenticalFramesRecursive(const KisNode *node, int time)
int end() const
bool isValid() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
Private(KisImageSP _image, const KisTimeSpan &_range, const QString &baseFilename, int _sequenceNumberingOffset, bool _onlyNeedsUniqueFrames, KisPropertiesConfigurationSP _exportConfiguration)