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"));
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);
260 m_page->ffmpegLocation->setFileName(likelyFFmpegPath);
262 m_page->ffmpegLocation->setReadOnlyText(
true);
284 m_page->chkIncludeAudio->setChecked(hasAudioLoaded);
290 QString *customFFMpegOptionsString,
296 QScopedPointer<KisVideoExportOptionsDialog> encoderConfigWidget(
300 encoderConfigWidget->setSupportsHDR(
true);
301 encoderConfigWidget->setConfiguration(cfg);
302 *customFFMpegOptionsString = encoderConfigWidget->customUserOptionsString();
303 *renderHDR = encoderConfigWidget->videoConfiguredForHDR();
309 return (type.startsWith(
"image/")
310 || (type.startsWith(
"application/") &&
311 !type.startsWith(
"application/x-spriter")));
318 supportedMimeTypes <<
"video/x-matroska";
319 supportedMimeTypes <<
"video/mp4";
320 supportedMimeTypes <<
"video/webm";
321 supportedMimeTypes <<
"image/gif";
322 supportedMimeTypes <<
"image/apng";
323 supportedMimeTypes <<
"image/webp";
324 supportedMimeTypes <<
"video/ogg";
326 return supportedMimeTypes;
331 Q_FOREACH(
const KoID &
encoder, encodersExpected ) {
332 if (encodersPresent.contains(
encoder.id())) {
345 Q_FOREACH(
const QString& mime, input) {
346 if ( mime ==
"video/x-matroska" ) {
353 }
else if (mime ==
"video/mp4") {
360 }
else if (mime ==
"video/webm") {
367 }
else if (mime ==
"image/gif") {
371 }
else if (mime ==
"image/apng") {
375 }
else if (mime ==
"image/webp") {
379 }
else if (mime ==
"video/ogg") {
391 return (mime ==
"image/png");
408 m_page->cmbRenderType->setDisabled(
true);
409 m_page->bnRenderOptions->setDisabled(
true);
411 QString previousMimeType =
m_page->cmbRenderType->currentData().toString();
413 m_page->cmbRenderType->clear();
419 ffmpegVersion = ffmpegJsonObj[
"enabled"].toBool() ? ffmpegJsonObj[
"version"].toString() : i18n(
"No valid FFmpeg binary supplied...");
424 QJsonObject codecjson = ffmpegJsonObj[
"codecs"].toObject()[codec].toObject();
425 if ( codecjson[
"encoding"].toBool() ) {
426 QJsonArray codecEncoders = codecjson[
"encoders"].toArray();
432 if (codecEncoders.size() == 0) {
433 codecEncoders.push_back(QJsonValue(codec));
436 Q_FOREACH(
const QJsonValue&
value, codecEncoders) {
454 int previousMimeTypeIndex = -1;
455 Q_FOREACH (
const QString &mime, supportedMimeTypes) {
457 if (description.isEmpty()) {
461 m_page->cmbRenderType->addItem(description, mime);
462 if (mime == previousMimeType) {
463 previousMimeTypeIndex =
m_page->cmbRenderType->count() - 1;
467 const int indexCount =
m_page->cmbRenderType->count();
468 if (indexCount > 0) {
469 if (previousMimeTypeIndex >= 0) {
470 m_page->cmbRenderType->setCurrentIndex(previousMimeTypeIndex % indexCount);
472 m_page->cmbRenderType->setCurrentIndex(0);
476 m_page->cmbRenderType->setDisabled(
false);
477 m_page->bnRenderOptions->setDisabled(
false);
492 const QString mimeType =
m_page->cmbRenderType->itemData(
m_page->cmbRenderType->currentIndex()).toString();
494 const QRegularExpression minVerFFMpegRX(R
"(^n{0,1}(?:[0-3]|4\.[01])[\.\-])");
495 const QRegularExpressionMatch minVerFFMpegMatch = minVerFFMpegRX.match(
ffmpegVersion);
499 if (mimeType ==
"image/gif" && minVerFFMpegMatch.hasMatch()) {
500 warnings << i18nc(
"ffmpeg warning checks",
"FFmpeg must be at least version 4.2+ for GIF transparency to work");
505 if (mimeType ==
"image/gif" &&
m_page->intFramesPerSecond->value() > 50) {
506 warnings << i18nc(
"ffmpeg warning checks",
507 "Animated GIF images cannot have a framerate higher than 50. The framerate will be reduced "
508 "to 50 frames per second");
511 m_page->lblWarnings->setVisible(!warnings.isEmpty());
513 if (!warnings.isEmpty()) {
514 QString text = QString(
"<p><b>%1</b>").arg(i18n(
"Warning(s):"));
516 Q_FOREACH (
const QString &warning, warnings) {
518 text.append(warning.toHtmlEscaped());
519 text.append(
"</li>");
521 text.append(
"</ul></p>");
522 m_page->lblWarnings->setText(text);
524 m_page->lblWarnings->setPixmap(
525 m_page->lblWarnings->style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(QSize(32, 32)));
535 if (!mimeType.isEmpty()) {
536 return QString(
"%1.%2").arg(QFileInfo(docFileName).completeBaseName(),
545 if (
m_page->cmbRenderType->count() == 0)
return;
547 const QString mimeType =
m_page->cmbRenderType->itemData(index).toString();
557 if (!
m_page->videoFilename->fileName().isEmpty()) {
558 const QFileInfo info = QFileInfo(
m_page->videoFilename->fileName());
559 const QString baseName = info.completeBaseName();
560 const QString path = info.path();
562 videoFileName = QString(
"%1%2%3.%4")
566 m_page->videoFilename->setFileName(videoFileName);
589 const int index =
m_page->cmbRenderType->currentIndex();
590 const QString mimetype =
m_page->cmbRenderType->itemData(index).toString();
615 if (dlg.exec() == QDialog::Accepted) {
622 encoderConfigWidget->deleteLater();
627 int index =
m_page->cmbMimetype->currentIndex();
631 QString mimetype =
m_page->cmbMimetype->itemData(index).toString();
636 if (frameExportConfigWidget) {
647 exportConfig->setProperty(
"forceSRGB",
false);
655 if (dlg.exec() == QDialog::Accepted) {
660 frameExportConfigWidget->hide();
662 frameExportConfigWidget->setParent(0);
663 frameExportConfigWidget->deleteLater();
710 if (forceNecessaryHDRSettings) {
712 cfg->setProperty(
"forceSRGB",
false);
713 cfg->setProperty(
"saveAsHDR",
true);
724 if (!ffmpegPath.isEmpty()) {
725 QFileInfo ffmpegBinary(ffmpegPath);
726 if (ffmpegBinary.exists() && ffmpegBinary.isExecutable()) {
727 QStringList commpressedFormats{
"zip",
"7z",
"tar.bz2"};
728 Q_FOREACH(
const QString& compressedFormat, commpressedFormats) {
729 if (ffmpegBinary.fileName().endsWith(compressedFormat)) {
734 }
else if (ffmpegBinary.exists()) {
744 QString fileName =
m_page->videoFilename->fileName();
746 if (fileName.isEmpty()) {
747 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"), i18n(
"Please enter a file name to render to."));
753 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."));
756 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."));
781 const bool willEncodeVideo =
m_page->shouldExportOnlyVideo->isChecked();
784 if (willEncodeVideo) {
786 m_page->cmbMimetype->setEnabled(
false);
787 m_page->cmbMimetype->setCurrentIndex(
m_page->cmbMimetype->findData(
"image/png"));
793 if (!
m_page->shouldExportOnlyVideo->isChecked() &&
794 !
m_page->shouldExportOnlyImageSequence->isChecked()) {
797 m_page->shouldExportOnlyImageSequence->setChecked(
true);
814 float newHeight =
m_page->intWidth->value() / aspectRatio ;
816 m_page->intHeight->setValue(newHeight);
827 float newWidth = aspectRatio *
m_page->intHeight->value();
829 m_page->intWidth->setValue(newWidth);
float value(const T *src, size_t ch)
bool meetsEncoderRequirementsForContainer(KisVideoExportOptionsDialog::ContainerType encoderType, const QStringList &encodersPresent)
QList< QString > QStringList
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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()
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())