9#include <QStandardPaths>
10#include <QPluginLoader>
17#include <klocalizedstring.h>
18#include <kpluginfactory.h>
49 , m_image(doc->image())
59 m_page->layout()->setContentsMargins(0, 0, 0, 0);
63 m_page->intStart->setMinimum(0);
67 m_page->intHeight->setMinimum(1);
68 m_page->intHeight->setMaximum(100000);
70 m_page->intWidth->setMinimum(1);
71 m_page->intWidth->setMaximum(100000);
75 const bool hasAudio = audioFiles.count() > 0;
76 m_page->chkIncludeAudio->setEnabled(hasAudio);
82 Q_FOREACH(
const QString &mime, mimes) {
84 if (description.isEmpty()) {
88 m_page->cmbMimetype->addItem(description, mime);
90 if (mime ==
"image/png") {
91 m_page->cmbMimetype->setCurrentIndex(
m_page->cmbMimetype->count() - 1);
95 m_page->cmbScaleFilter->addItem(i18nc(
"bicubic filtering",
"bicubic"),
"bicubic");
96 m_page->cmbScaleFilter->addItem(i18nc(
"bilinear filtering",
"bilinear"),
"bilinear");
97 m_page->cmbScaleFilter->addItem(i18nc(
"lanczos3 filtering",
"lanczos3"),
"lanczos");
98 m_page->cmbScaleFilter->addItem(i18nc(
"nearest neighbor filtering",
"neighbor"),
"neighbor");
99 m_page->cmbScaleFilter->addItem(i18nc(
"spline filtering",
"spline"),
"spline");
105 m_page->cmbRenderType->setPlaceholderText(i18nc(
"Not applicable. No render types without valid ffmpeg path.",
"N/A"));
116 connect(
m_page->ffmpegLocation, SIGNAL(fileSelected(QString)), SLOT(
setFFmpegPath(QString)));
149 if (cfgFFmpegPath.isEmpty()) {
151 cfgFFmpegPath = (ffmpegInfo[
"enabled"].toBool()) ? ffmpegInfo[
"path"].toString() :
"";
156 QString likelyFFmpegPath = [&]() {
158 if (!lastUsedOptions.
ffmpegPath.isEmpty() && QFileInfo(lastUsedOptions.
ffmpegPath).isExecutable()) {
163 if (!cfgFFmpegPath.isEmpty() && QFileInfo(cfgFFmpegPath).isExecutable()) {
164 return cfgFFmpegPath;
168 QString systemFFmpeg = QStandardPaths::findExecutable(
"ffmpeg");
169 if (!systemFFmpeg.isEmpty() && QFileInfo(systemFFmpeg).isExecutable()) {
175 return (ffmpegJsonObj[
"enabled"].toBool()) ? ffmpegJsonObj[
"path"].toString() :
"";
178 if (!likelyFFmpegPath.isEmpty() && QFileInfo(likelyFFmpegPath).isExecutable()) {
213 for (
int i = 0; i <
m_page->cmbMimetype->count(); ++i) {
215 m_page->cmbMimetype->setCurrentIndex(i);
220 for (
int i = 0; i <
m_page->cmbScaleFilter->count(); ++i) {
221 if (
m_page->cmbScaleFilter->itemData(i).toString() == lastUsedOptions.
scaleFilter) {
222 m_page->cmbScaleFilter->setCurrentIndex(i);
228 for (
int i = 0; i <
m_page->cmbRenderType->count(); ++i) {
230 m_page->cmbRenderType->setCurrentIndex(i);
241 m_page->shouldExportOnlyVideo->setChecked(
false);
242 m_page->shouldExportOnlyVideo->setEnabled(
false);
243 m_page->shouldExportOnlyVideo->setVisible(
false);
244 m_page->shouldExportOnlyImageSequence->setChecked(
true);
245 m_page->shouldExportOnlyImageSequence->setCheckable(
false);
269 m_page->ffmpegLocation->setFileName(likelyFFmpegPath);
271 m_page->ffmpegLocation->setReadOnlyText(
true);
293 m_page->chkIncludeAudio->setChecked(hasAudioLoaded);
299 QString *customFFMpegOptionsString,
305 QScopedPointer<KisVideoExportOptionsDialog> encoderConfigWidget(
309 encoderConfigWidget->setSupportsHDR(
true);
310 encoderConfigWidget->setConfiguration(cfg);
311 *customFFMpegOptionsString = encoderConfigWidget->customUserOptionsString();
312 *renderHDR = encoderConfigWidget->videoConfiguredForHDR();
318 return (type.startsWith(
"image/")
319 || (type.startsWith(
"application/") &&
320 !type.startsWith(
"application/x-spriter")));
327 supportedMimeTypes <<
"video/x-matroska";
328 supportedMimeTypes <<
"video/mp4";
329 supportedMimeTypes <<
"video/webm";
330 supportedMimeTypes <<
"image/gif";
331 supportedMimeTypes <<
"image/apng";
332 supportedMimeTypes <<
"image/webp";
333 supportedMimeTypes <<
"video/ogg";
335 return supportedMimeTypes;
340 Q_FOREACH(
const KoID &
encoder, encodersExpected ) {
341 if (encodersPresent.contains(
encoder.id())) {
354 Q_FOREACH(
const QString& mime, input) {
355 if ( mime ==
"video/x-matroska" ) {
362 }
else if (mime ==
"video/mp4") {
369 }
else if (mime ==
"video/webm") {
376 }
else if (mime ==
"image/gif") {
380 }
else if (mime ==
"image/apng") {
384 }
else if (mime ==
"image/webp") {
388 }
else if (mime ==
"video/ogg") {
400 return (mime ==
"image/png");
417 m_page->cmbRenderType->setDisabled(
true);
418 m_page->bnRenderOptions->setDisabled(
true);
420 QString previousMimeType =
m_page->cmbRenderType->currentData().toString();
422 m_page->cmbRenderType->clear();
428 ffmpegVersion = ffmpegJsonObj[
"enabled"].toBool() ? ffmpegJsonObj[
"version"].toString() : i18n(
"No valid FFmpeg binary supplied...");
433 QJsonObject codecjson = ffmpegJsonObj[
"codecs"].toObject()[codec].toObject();
434 if ( codecjson[
"encoding"].toBool() ) {
435 QJsonArray codecEncoders = codecjson[
"encoders"].toArray();
441 if (codecEncoders.size() == 0) {
442 codecEncoders.push_back(QJsonValue(codec));
445 Q_FOREACH(
const QJsonValue&
value, codecEncoders) {
463 int previousMimeTypeIndex = -1;
464 Q_FOREACH (
const QString &mime, supportedMimeTypes) {
466 if (description.isEmpty()) {
470 m_page->cmbRenderType->addItem(description, mime);
471 if (mime == previousMimeType) {
472 previousMimeTypeIndex =
m_page->cmbRenderType->count() - 1;
476 const int indexCount =
m_page->cmbRenderType->count();
477 if (indexCount > 0) {
478 if (previousMimeTypeIndex >= 0) {
479 m_page->cmbRenderType->setCurrentIndex(previousMimeTypeIndex % indexCount);
481 m_page->cmbRenderType->setCurrentIndex(0);
485 m_page->cmbRenderType->setDisabled(
false);
486 m_page->bnRenderOptions->setDisabled(
false);
501 const QString mimeType =
m_page->cmbRenderType->itemData(
m_page->cmbRenderType->currentIndex()).toString();
503 const QRegularExpression minVerFFMpegRX(R
"(^n{0,1}(?:[0-3]|4\.[01])[\.\-])");
504 const QRegularExpressionMatch minVerFFMpegMatch = minVerFFMpegRX.match(
ffmpegVersion);
508 if (mimeType ==
"image/gif" && minVerFFMpegMatch.hasMatch()) {
509 warnings << i18nc(
"ffmpeg warning checks",
"FFmpeg must be at least version 4.2+ for GIF transparency to work");
514 if (mimeType ==
"image/gif" &&
m_page->intFramesPerSecond->value() > 50) {
515 warnings << i18nc(
"ffmpeg warning checks",
516 "Animated GIF images cannot have a framerate higher than 50. The framerate will be reduced "
517 "to 50 frames per second");
520 m_page->lblWarnings->setVisible(!warnings.isEmpty());
522 if (!warnings.isEmpty()) {
523 QString text = QString(
"<p><b>%1</b>").arg(i18n(
"Warning(s):"));
525 Q_FOREACH (
const QString &warning, warnings) {
527 text.append(warning.toHtmlEscaped());
528 text.append(
"</li>");
530 text.append(
"</ul></p>");
531 m_page->lblWarnings->setText(text);
533 m_page->lblWarnings->setPixmap(
534 m_page->lblWarnings->style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(QSize(32, 32)));
544 if (!mimeType.isEmpty()) {
545 return QString(
"%1.%2").arg(QFileInfo(docFileName).completeBaseName(),
554 if (
m_page->cmbRenderType->count() == 0)
return;
556 const QString mimeType =
m_page->cmbRenderType->itemData(index).toString();
566 if (!
m_page->videoFilename->fileName().isEmpty()) {
567 const QFileInfo info = QFileInfo(
m_page->videoFilename->fileName());
568 const QString baseName = info.completeBaseName();
569 const QString path = info.path();
571 videoFileName = QString(
"%1%2%3.%4")
575 m_page->videoFilename->setFileName(videoFileName);
598 const int index =
m_page->cmbRenderType->currentIndex();
599 const QString mimetype =
m_page->cmbRenderType->itemData(index).toString();
624 if (dlg.exec() == QDialog::Accepted) {
631 encoderConfigWidget->deleteLater();
636 int index =
m_page->cmbMimetype->currentIndex();
640 QString mimetype =
m_page->cmbMimetype->itemData(index).toString();
645 if (frameExportConfigWidget) {
656 exportConfig->setProperty(
"forceSRGB",
false);
664 if (dlg.exec() == QDialog::Accepted) {
669 frameExportConfigWidget->hide();
671 frameExportConfigWidget->setParent(0);
672 frameExportConfigWidget->deleteLater();
724 if (forceNecessaryHDRSettings) {
726 cfg->setProperty(
"forceSRGB",
false);
727 cfg->setProperty(
"saveAsHDR",
true);
738 if (!ffmpegPath.isEmpty()) {
739 QFileInfo ffmpegBinary(ffmpegPath);
740 if (ffmpegBinary.exists() && ffmpegBinary.isExecutable()) {
741 QStringList commpressedFormats{
"zip",
"7z",
"tar.bz2"};
742 Q_FOREACH(
const QString& compressedFormat, commpressedFormats) {
743 if (ffmpegBinary.fileName().endsWith(compressedFormat)) {
748 }
else if (ffmpegBinary.exists()) {
758 QString fileName =
m_page->videoFilename->fileName();
760 if (fileName.isEmpty()) {
761 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"), i18n(
"Please enter a file name to render to."));
767 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"), i18n(
"The FFmpeg that you've given us appears to be compressed. Please try to extract FFmpeg from the archive first."));
770 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"), i18n(
"The FFmpeg that you've given us appears to be invalid. Please select the correct location of an FFmpeg executable on your system."));
795 if (
m_page->shouldExportOnlyVideo->isChecked()) {
797 m_page->cmbMimetype->setEnabled(
false);
798 m_page->cmbMimetype->setCurrentIndex(
m_page->cmbMimetype->findData(
"image/png"));
804 if (!
m_page->shouldExportOnlyVideo->isChecked() &&
805 !
m_page->shouldExportOnlyImageSequence->isChecked()) {
808 m_page->shouldExportOnlyImageSequence->setChecked(
true);
812 m_page->shouldExportOnlyImageSequence->setChecked(
true);
829 float newHeight =
m_page->intWidth->value() / aspectRatio ;
831 m_page->intHeight->setValue(newHeight);
842 float newWidth = aspectRatio *
m_page->intHeight->value();
844 m_page->intWidth->setValue(newWidth);
float value(const T *src, size_t ch)
bool meetsEncoderRequirementsForContainer(KisVideoExportOptionsDialog::ContainerType encoderType, const QStringList &encodersPresent)
QList< QString > QStringList
KisAcyclicSignalConnector * createCoordinatedConnector()
create a coordinated connector that can be used for extending the number of self-locking connection.
void connectBackwardInt(QObject *sender, const char *signal, QObject *receiver, const char *method)
void connectForwardInt(QObject *sender, const char *signal, QObject *receiver, const char *method)
QString resolveAbsoluteDocumentFilePath(const QString &documentPath) const
void fromProperties(KisPropertiesConfigurationSP config)
KisPropertiesConfigurationSP frameExportConfig
KisPropertiesConfigurationSP toProperties() const
QString customFFMpegOptions
bool shouldDeleteSequence
bool wantsOnlyUniqueFrameSequence
void setFFMpegLocation(const QString &value)
KisPropertiesConfigurationSP exportConfiguration(const QString &filterId, bool defaultValue=false) const
QString ffmpegLocation(bool defaultValue=false) const
void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const
void initializeRenderSettings(const KisDocument &doc, const KisAnimationRenderingOptions &lastUsedOptions)
bool m_wantsRenderWithHDR
QStringList filterMimeTypeListByAvailableEncoders(const QStringList &mimeTypes)
void frameRateChanged(int framerate)
KisDlgAnimationRenderer(KisDocument *doc, QWidget *parent=0)
void slotExportTypeChanged()
static constexpr bool PLATFORM_SUPPORTS_FFMPEG
void sequenceMimeTypeOptionsClicked()
sequenceMimeTypeSelected calls the dialog for the export widget.
KisAnimationRenderingOptions getEncoderOptions() const
void setFFmpegPath(const QString &path)
void slotLockAspectRatioDimensionsHeight(int height)
QString m_customFFMpegOptionsString
void slotLockAspectRatioDimensionsWidth(int width)
void ffmpegWarningCheck()
static void getDefaultVideoEncoderOptions(const QString &mimeType, KisPropertiesConfigurationSP cfg, const QStringList &availableEncoders, QString *customFFMpegOptionsString, bool *forceHDRVideo)
void slotDialogAccepted()
void selectRenderOptions()
static KisPropertiesConfigurationSP loadLastConfiguration(QString configurationID)
WdgAnimationRenderer * m_page
static QString defaultVideoFileName(KisDocument *doc, const QString &mimeType)
FFmpegValidationResult validateFFmpeg(const QString &ffmpegPath)
static bool imageMimeSupportsHDR(QString &hdr)
static QStringList makeVideoMimeTypesList()
QMap< QString, QStringList > ffmpegEncoderTypes
~KisDlgAnimationRenderer() override
static void saveLastUsedConfiguration(QString configurationID, KisPropertiesConfigurationSP config)
static void filterSequenceMimeTypes(QStringList &mimeTypes)
void slotButtonClicked(int button) override
void selectRenderType(int i)
QString localFilePath() const
static QByteArray nativeFormatMimeType()
QVector< QFileInfo > getAudioTracks() const
static QStringList getSupportedCodecs(const QJsonObject &ffmpegJsonProcessInput)
static QJsonObject findFFMpeg(const QString &customLocation)
void setExportSequenceBaseName(const QString &baseName)
QString exportSequenceBaseName()
const KisTimeSpan & activePlaybackRange() const
activePlaybackRange
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
QString exportSequenceFilePath()
int exportInitialFrameNumber()
void setExportInitialFrameNumber(const int frameNum)
void setExportSequenceFilePath(const QString &filePath)
KisImageAnimationInterface * animationInterface() const
static KisImportExportFilter * filterForMimeType(const QString &mimetype, Direction direction)
filterForMimeType loads the relevant import/export plugin and returns it. The caller is responsible f...
static void fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration, KisImageSP image)
static QStringList supportedMimeTypes(Direction direction)
static QStringList suffixesForMimeType(const QString &mimeType)
static QString descriptionForMimeType(const QString &mimeType)
Find the user-readable description for the given mimetype.
KisPropertiesConfigurationSP configuration() const override
static ContainerType mimeToContainer(const QString &mimeType)
QString customUserOptionsString() const
void setHDRConfiguration(bool value)
void setConfiguration(const KisPropertiesConfigurationSP config) override
bool videoConfiguredForHDR() const
static QVector< KoID > encoderIdentifiers(ContainerType type)
void setSupportsHDR(bool value)
A dialog base class with standard buttons and predefined layouts.
virtual void slotButtonClicked(int button)
QPushButton * button(ButtonCode id) const
void setMainWidget(QWidget *widget)
virtual void setCaption(const QString &caption)
void setButtons(ButtonCodes buttonMask)
void setDefaultButton(ButtonCode id)
@ Ok
Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
@ Cancel
Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
Encoder * encoder(Imf::OutputFile &file, const ExrPaintLayerSaveInfo &info, int width)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
QString button(const QWheelEvent &ev)
auto filterContainer(C &container, KeepIfFunction keepIf) -> decltype(bool(keepIf(container[0])), void())