Render an animation to file on disk. returns TRUE on success, FALSE on error or cancellation.
55 {
56 bool isTemporaryFramesDirectory = false;
57 QString framesDirectory;
58#ifdef Q_OS_ANDROID
59
60
61
62
63
66 return false;
67 }
68 }
else if (encoderOptions.
directory.isEmpty()) {
69 return false;
70 }
71
72
73
74
75
76 std::unique_ptr<QTemporaryDir> tempDir;
78 tempDir = std::make_unique<QTemporaryDir>();
80 framesDirectory = tempDir->path();
81 isTemporaryFramesDirectory = true;
82 } else {
83 framesDirectory = encoderOptions.
directory;
84 }
85#else
87#endif
88
91 const QString baseFileName = QString(
"%1/%2.%3").arg(framesDirectory, encoderOptions.
basename, extension);
92
93#ifdef Q_OS_ANDROID
94 QString videoType = encoderOptions.videoFormatKey;
95#else
97#endif
100 encoderOptions.
width = encoderOptions.
width + (encoderOptions.
width & 0x1);
102 }
103 }
104
105 const QSize scaledSize = doc->
image()->
bounds().size().scaled(encoderOptions.
width, encoderOptions.
height, Qt::IgnoreAspectRatio);
106
109 QString type = looksLikeMp4(videoType) ? "Mpeg4 (.mp4) " : "Matroska (.mkv) ";
110
111 qWarning() << type <<"requires width and height to be even, resize and try again!";
112 doc->
setErrorMessage(i18n(
"%1 requires width and height to be even numbers. Please resize or crop the image before exporting.", type));
113 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Could not render animation:\n%1", doc->
errorMessage()));
114
115 return false;
116 }
117 }
118
119 const bool batchMode = false;
123 baseFileName,
127 exporter.setBatchMode(batchMode);
128
131
133
134
137
138 const QString savedFilesMask = exporter.savedFilesMask();
139
141 bool videoFileWriteAllowed = true;
142
143
144
145
146#ifndef Q_OS_ANDROID
149
150 const QFileInfo videoOutputFile(videoOutputFilePath);
151 QDir outputDir(videoOutputFile.absolutePath());
152
153 if (!outputDir.exists()) {
154 outputDir.mkpath(videoOutputFile.absolutePath());
155 }
157
158
159 if (videoOutputFile.exists()) {
160 QMessageBox videoOverwritePrompt;
161
162 videoOverwritePrompt.setText(i18n("Overwrite existing video?"));
163 videoOverwritePrompt.setInformativeText(i18n("A file already exists at the path where you want to render your video [%1]... \n\
164 Are you sure you want to overwrite the existing file?", videoOutputFilePath));
165 videoOverwritePrompt.setStandardButtons(QMessageBox::Ok | QMessageBox::Abort);
166
167 videoFileWriteAllowed = videoOverwritePrompt.exec() == QMessageBox::Ok ? true : false;
168 }
169#endif
170
171
172 if (videoFileWriteAllowed) {
174
175
176
177
178#ifndef Q_OS_ANDROID
179 QFile videoFile(videoOutputFilePath);
180 if (!videoFile.open(QIODevice::WriteOnly)) {
181 qWarning() << "Could not open" << videoFile.fileName() << "for writing! Do you have permission to write to this file?";
183 } else {
184 videoFile.close();
185 }
186#endif
187
188 if (exportResult.
isOk()) {
190 exportResult =
encoder->convert(doc,
191 framesDirectory,
192 savedFilesMask,
193 exporter.savedFiles(),
194 encoderOptions,
195 batchMode);
196 }
197
198 if (!exportResult.
isOk()) {
199 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Could not render animation:\n%1", exportResult.
errorMessage()));
200
201 delayReturnSuccess = false;
202 }
203 }
204 }
205
206
207 if (!isTemporaryFramesDirectory) {
208 QDir
d(framesDirectory);
209
210#ifdef Q_OS_ANDROID
211 bool shouldDeleteSequence = false;
212#else
214#endif
215 if (shouldDeleteSequence || !delayReturnSuccess) {
217
218 Q_FOREACH(const QString &f, savedFiles) {
221 }
222 }
224 const QStringList fileNames = exporter.savedFiles();
225 const QStringList uniqueFrameNames = exporter.savedUniqueFiles();
226
227 Q_FOREACH(const QString &f, fileNames) {
228 if (!uniqueFrameNames.contains(f)) {
230 }
231 }
232 }
233
234
235#ifndef Q_OS_ANDROID
237
238 Q_FOREACH(const QString &f, paletteFiles) {
240 }
241#endif
242 }
244 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Rendering error"), "Animation frame rendering has timed out. Output files are incomplete.\nTry to increase \"Frame Rendering Timeout\" or reduce \"Frame Rendering Clones Limit\" in Krita settings");
246 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Rendering error"), i18n("Failed to render animation frames! Output files are incomplete."));
247 }
248
249 return delayReturnSuccess;
250}
QList< QString > QStringList
RenderMode renderMode() const
KisPropertiesConfigurationSP frameExportConfig
QString resolveAbsoluteVideoFilePath(const QString &documentPath) const
QString resolveAbsoluteFramesDirectory(const QString &documentPath) const
bool shouldDeleteSequence
bool wantsOnlyUniqueFrameSequence
void setErrorMessage(const QString &errMsg)
QString errorMessage() const
QRect bounds() const override
QString errorMessage() const
KisViewManager * viewManager
static QStringList suffixesForMimeType(const QString &mimeType)
static KisTimeSpan fromTimeToTime(int start, int end)
KisMainWindow * mainWindow() const
Encoder * encoder(Imf::OutputFile &file, const ExrPaintLayerSaveInfo &info, int width)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
bool hasEvenDimensions(int width, int height)
bool mustHaveEvenDimensions(const QString &videoType, KisAnimationRenderingOptions::RenderMode renderMode)