Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMediaEncoderWrapper.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: GPL-3.0-or-later
3 */
4#include <QAtomicInt>
5#include <QCoreApplication>
6#include <QImage>
7#include <QImageReader>
8#include <QProgressDialog>
9#include <QRunnable>
10#include <QThreadPool>
11
12#include <functional>
13#include <memory>
14
15#include <klocalizedstring.h>
16
18
19#include <kis_assert.h>
20#include <kis_debug.h>
21
22#ifdef Q_OS_ANDROID
24#endif
26
31
33{
34 Q_EMIT sigStarted();
35 QString errorMessage;
36 EncodeResult result = prepareAndEncode(errorMessage);
37 if (result == EncodeResult::Completed) {
38 Q_EMIT sigCompleted();
39 } else if (result == EncodeResult::Cancelled) {
40 Q_EMIT sigCancelled();
41 } else {
42 Q_EMIT sigFailed(errorMessage);
43 }
44}
45
47{
48 QImageReader reader(m_path);
49 if (reader.read(&outImage)) {
50 // Should be non-null on success, but we'll guard against mayhem.
51 if (outImage.isNull() || outImage.size().isEmpty()) {
52 warnFile.nospace() << "Got null image reading frame from file '" << m_path << "'";
53 return false;
54 } else {
55 return true;
56 }
57 } else {
58 warnFile.nospace() << "Error " << reader.error() << " reading frame from file '" << m_path
59 << "': " << reader.errorString();
60 // Again, should be null anyway, but let's be extra sure.
61 outImage = QImage();
62 return false;
63 }
64}
65
71
73{
74 int inputFileCount = m_settings.inputFiles.size();
75 if (inputFileCount == 0) {
76 return false;
77 }
78
79 int lastIndex = inputFileCount - 1;
82 int seconds = m_settings.firstFrameSec;
83 if (seconds > 0) {
84 outFrame = Frame(m_settings.inputFiles[lastIndex], seconds * m_settings.outputFps);
85 return true;
86 }
87 }
88
89 double inputFrameDuration = 1.0 / double(m_settings.inputFps);
90 double outputFrameDuration = 1.0 / double(m_settings.outputFps);
91 while (m_inputFileIndex < inputFileCount) {
92 int fileIndex = m_inputFileIndex;
94
95 int instances = 0;
96 double nextInputTime = double(m_inputFileIndex) * inputFrameDuration;
97 while (m_inputTime < nextInputTime) {
98 ++instances;
99 m_inputTime += outputFrameDuration;
100 }
101
102 if (instances != 0) {
103 // GIF must spin through the input frames twice, once to generate a
104 // palette and then again to actually render the animated image. It
105 // sets the multiplier to 0.5 so that the progress bar shows values
106 // that make sense for the user, rather than reaching 100% halfway.
107 m_outputFrameNo += double(instances) * m_outputFrameNoMultiplier;
109
110 // If we're on the last frame, we can append the linger time.
111 if (fileIndex == lastIndex && m_needsLingerAfter) {
112 m_needsLingerAfter = false;
113 instances += qMax(0, m_settings.lastFrameSec) * m_settings.outputFps;
114 }
115
116 outFrame = Frame(m_settings.inputFiles[fileIndex], instances);
117 return true;
118 }
119 }
120
121 if (m_needsLingerAfter) {
122 m_needsLingerAfter = false;
123 int seconds = m_settings.lastFrameSec;
124 if (seconds > 0) {
125 outFrame = Frame(m_settings.inputFiles[lastIndex], seconds * m_settings.outputFps);
126 return true;
127 }
128 }
129
130 return false;
131}
132
140
142{
143 if (m_cancel) {
145 }
146
147 if (m_settings.inputFiles.isEmpty()) {
148 outErrorMessage = i18n("No frame files found");
150 }
151
153 return encode(outErrorMessage);
154}
155
157 : QObject(parent)
158{
159}
160
165
167{
168 reset();
169
172 if (runnable) {
173 connect(
174 runnable,
176 this,
177 [&errorCode] {
179 },
180 Qt::DirectConnection);
181 connect(
182 runnable,
184 this,
185 [&errorCode] {
187 },
188 Qt::DirectConnection);
189 connect(
190 runnable,
192 this,
193 [&errorCode](const QString &errorMessage) {
194 errorCode = KisImportExportErrorCode(errorMessage);
195 },
196 Qt::DirectConnection);
197 connect(this,
199 runnable,
201 Qt::DirectConnection);
202
203 QProgressDialog *progressDlg;
204 if (batchMode) {
205 progressDlg = nullptr;
206 } else {
207 progressDlg = new QProgressDialog;
208 progressDlg->setLabelText(i18n("Rendering animation..."));
209 connect(
210 runnable,
212 this,
213 [progressDlg, frameCount = settings.inputFiles.size()](int frameNo) {
214 int progress;
215 if (frameNo < 0) {
216 progress = 0;
217 } else if (frameNo >= frameCount) {
218 progress = 100;
219 } else {
220 progress = qRound(qreal(frameNo) / qreal(frameCount) * 100.0);
221 }
222 progressDlg->setValue(progress);
223 QCoreApplication::processEvents();
224 },
225 Qt::DirectConnection);
226 connect(progressDlg,
227 &QProgressDialog::canceled,
228 this,
230 Qt::DirectConnection);
231 progressDlg->show();
232 QCoreApplication::processEvents();
233 }
234
235 m_runnable = runnable;
236 runnable->run();
237 delete runnable;
238 delete progressDlg;
239 }
240 return errorCode;
241}
242
244{
245 reset();
246
248 if (runnable) {
249 connect(runnable,
251 this,
253 Qt::QueuedConnection);
254 connect(runnable,
256 this,
258 Qt::QueuedConnection);
259 connect(runnable,
261 this,
263 Qt::QueuedConnection);
264 connect(runnable,
266 this,
268 Qt::QueuedConnection);
269 connect(runnable,
271 this,
273 Qt::QueuedConnection);
274 connect(this,
276 runnable,
278 Qt::QueuedConnection);
279
280 m_runnable = runnable;
281 runnable->setAutoDelete(true);
282 QThreadPool::globalInstance()->start(runnable);
283
284 } else {
285 Q_EMIT sigFinishedWithError(i18n("No encoder for the selected format found"));
286 }
287}
288
290{
291 KisMediaEncoderRunnable *runnable = m_runnable.data();
292 if (runnable) {
293 m_runnable.clear();
294 Q_EMIT sigCancelRequested();
295 disconnect(runnable);
296 }
297}
298
300{
301 static QVector<KisMediaEncoderFormat *> *supportedFormats;
302 if (!supportedFormats) {
303 supportedFormats = new QVector<KisMediaEncoderFormat *>();
304#ifdef Q_OS_ANDROID
306#endif
308 }
309 return *supportedFormats;
310}
311
313{
315 if (format->key() == key) {
316 return format;
317 }
318 }
319 return nullptr;
320}
321
323{
325
326#ifdef Q_OS_ANDROID
327 {
329 if (androidRunnable) {
330 return androidRunnable;
331 }
332 }
333#endif
334 {
336 if (libavRunnable) {
337 return libavRunnable;
338 }
339 }
340
341 return nullptr;
342}
static void getSupportedFormats(QVector< KisMediaEncoderFormat * > &outSupportedFormats)
static KisAndroidMediaEncoderRunnable * create(const KisMediaEncoderWrapperSettings &settings, QObject *parent=nullptr)
static KisLibavMediaEncoderRunnable * create(const KisMediaEncoderWrapperSettings &settings, QObject *parent=nullptr)
static void getSupportedFormats(QVector< KisMediaEncoderFormat * > &outSupportedFormats)
bool readImage(QImage &outImage) const
KisMediaEncoderRunnable(const KisMediaEncoderWrapperSettings &settings, QObject *parent)
const KisMediaEncoderWrapperSettings settings()
bool nextFrame(Frame &outFrame)
KisMediaEncoderWrapperSettings m_settings
void sigFailed(const QString &errorMessage)
EncodeResult prepareAndEncode(QString &outErrorMessage)
virtual EncodeResult encode(QString &outErrorMessage)=0
void sigProgressUpdated(int frameNo)
static KisMediaEncoderFormat * getFormatByKey(const QString &key)
void sigFinishedWithError(QString message)
QPointer< KisMediaEncoderRunnable > m_runnable
KisMediaEncoderWrapper(QObject *parent=nullptr)
void sigProgressUpdated(int frameNo)
static const QVector< KisMediaEncoderFormat * > & getSupportedFormats()
static KisMediaEncoderRunnable * makeSupportedRunnable(const KisMediaEncoderWrapperSettings &settings)
KisImportExportErrorCode start(const KisMediaEncoderWrapperSettings &settings, bool batchMode)
void startNonBlocking(const KisMediaEncoderWrapperSettings &settings)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define warnFile
Definition kis_debug.h:99