From 3d2774a74d01cce8c5735c4928ab2b1264e0dfda Mon Sep 17 00:00:00 2001 From: justburner Date: Mon, 3 Jan 2022 23:37:07 +0000 Subject: [PATCH] FFMPEG GIF Export enhancements --- toonz/sources/image/ffmpeg/tiio_gif.cpp | 134 ++++++++++++++----- toonz/sources/image/ffmpeg/tiio_gif.h | 9 +- toonz/sources/include/tproperty.h | 5 +- toonz/sources/toonz/dvwidgets.cpp | 23 ++++ toonz/sources/toonz/dvwidgets.h | 16 +++ toonz/sources/toonz/formatsettingspopups.cpp | 32 +++++ toonz/sources/toonz/formatsettingspopups.h | 1 + toonz/sources/toonzlib/outputproperties.cpp | 9 +- 8 files changed, 191 insertions(+), 38 deletions(-) diff --git a/toonz/sources/image/ffmpeg/tiio_gif.cpp b/toonz/sources/image/ffmpeg/tiio_gif.cpp index d2d5a258..469f3f59 100644 --- a/toonz/sources/image/ffmpeg/tiio_gif.cpp +++ b/toonz/sources/image/ffmpeg/tiio_gif.cpp @@ -43,9 +43,16 @@ TLevelWriterGif::TLevelWriterGif(const TFilePath &path, TPropertyGroup *winfo) TBoolProperty *looping = (TBoolProperty *)m_properties->getProperty("Looping"); m_looping = looping->getValue(); - TBoolProperty *palette = - (TBoolProperty *)m_properties->getProperty("Generate Palette"); - m_palette = palette->getValue(); + TEnumProperty *modeProp = + dynamic_cast(m_properties->getProperty("Mode")); + m_mode = 0; + if (modeProp) { + m_mode = modeProp->getIndex(); + } + std::string maxcolors = + m_properties->getProperty("Max Colors")->getValueAsString(); + m_maxcolors = QString::fromStdString(maxcolors).toInt(); + ffmpegWriter = new Ffmpeg(); ffmpegWriter->setPath(m_path); // m_frameCount = 0; @@ -66,42 +73,72 @@ TLevelWriterGif::~TLevelWriterGif() { // set scaling outLx = m_lx * m_scale / 100; outLy = m_ly * m_scale / 100; + /* // ffmpeg doesn't like resolutions that aren't divisible by 2. if (outLx % 2 != 0) outLx++; if (outLy % 2 != 0) outLy++; + */ + double framerate = (m_frameRate < 1.0 ? 1.0 : m_frameRate); - QString palette; - QString filters = "scale=" + QString::number(outLx) + ":-1:flags=lanczos"; - QString paletteFilters = filters + " [x]; [x][1:v] paletteuse"; - if (m_palette) { - palette = ffmpegWriter->getFfmpegCache().getQString() + "//" + - QString::fromStdString(m_path.getName()) + "palette.png"; - palettePreIArgs << "-v"; - palettePreIArgs << "warning"; + QString filters = "fps=" + QString::number(framerate) + + ",scale=" + QString::number(outLx) + ":" + + QString::number(outLy) + ":flags=lanczos"; - palettePostIArgs << "-vf"; - palettePostIArgs << filters + ",palettegen"; - palettePostIArgs << palette; + // 1 = "dither=sierra2_4a" is default + const char *ditherConsts[4] = {"none", "sierra2_4a", "bayer:bayer_scale=2", + "bayer:bayer_scale=1"}; - // write the palette - ffmpegWriter->runFfmpeg(palettePreIArgs, palettePostIArgs, false, true, - true); - ffmpegWriter->addToCleanUp(palette); + // Please be careful when moving items, logic AND 3 requires alignment + switch (m_mode) { + case 0: + case 1: + case 2: + case 3: + filters += ",split[o1][o2];[o1]palettegen"; // "stats_mode=full" is default + if (m_maxcolors != 256) { + filters += "=max_colors=" + QString::number(m_maxcolors); + } + filters += "[p];[o2]fifo[o3];[o3][p]paletteuse"; + if ((m_mode & 3) != 1) { + filters += "=dither=" + QString(ditherConsts[m_mode & 3]); + } + break; + case 4: + case 5: + case 6: + case 7: + filters += ",split[o1][o2];[o1]palettegen=stats_mode=diff"; + if (m_maxcolors != 256) { + filters += ":max_colors=" + QString::number(m_maxcolors); + } + filters += "[p];[o2]fifo[o3];[o3][p]paletteuse"; + if ((m_mode & 3) != 1) { + filters += "=dither=" + QString(ditherConsts[m_mode & 3]); + } + break; + case 8: + case 9: + case 10: + case 11: + filters += ",split[o1][o2];[o1]palettegen=stats_mode=single"; + if (m_maxcolors != 256) { + filters += ":max_colors=" + QString::number(m_maxcolors); + } + filters += "[p];[o2]fifo[o3];[o3][p]paletteuse=new=1"; + if ((m_mode & 3) != 1) { + filters += ":dither=" + QString(ditherConsts[m_mode & 3]); + } + break; + default: + break; } preIArgs << "-v"; preIArgs << "warning"; - preIArgs << "-r"; - preIArgs << QString::number((m_frameRate < 1 ? 12.0 : m_frameRate)); - if (m_palette) { - postIArgs << "-i"; - postIArgs << palette; - postIArgs << "-lavfi"; - postIArgs << paletteFilters; - } else { - postIArgs << "-lavfi"; - postIArgs << filters; - } + postIArgs << "-vf"; + postIArgs << filters; + postIArgs << "-gifflags"; + postIArgs << "0"; if (!m_looping) { postIArgs << "-loop"; @@ -247,16 +284,49 @@ TImageP TLevelReaderGif::load(int frameIndex) { Tiio::GifWriterProperties::GifWriterProperties() : m_scale("Scale", 1, 100, 100) , m_looping("Looping", true) - , m_palette("Generate Palette", true) { + , m_mode("Mode") + , m_maxcolors("Max Colors", 2, 256, 256) { + // Set values for mode + m_mode.addValue(L"GLOBAL0"); + m_mode.addValue(L"GLOBAL1"); + m_mode.addValue(L"GLOBAL2"); + m_mode.addValue(L"GLOBAL3"); + m_mode.addValue(L"DIFF0"); + m_mode.addValue(L"DIFF1"); + m_mode.addValue(L"DIFF2"); + m_mode.addValue(L"DIFF3"); + m_mode.addValue(L"NEW0"); + m_mode.addValue(L"NEW1"); + m_mode.addValue(L"NEW2"); + m_mode.addValue(L"NEW3"); + m_mode.addValue(L"NOPAL"); + + // Translate values + m_mode.setItemUIName(L"GLOBAL0", tr("Global Palette")); + m_mode.setItemUIName(L"GLOBAL1", tr("Global Palette + Sierra Dither")); + m_mode.setItemUIName(L"GLOBAL2", tr("Global Palette + Bayer2 Dither")); + m_mode.setItemUIName(L"GLOBAL3", tr("Global Palette + Bayer1 Dither")); + m_mode.setItemUIName(L"DIFF0", tr("Diff Palette")); + m_mode.setItemUIName(L"DIFF1", tr("Diff Palette + Sierra Dither")); + m_mode.setItemUIName(L"DIFF2", tr("Diff Palette + Bayer2 Dither")); + m_mode.setItemUIName(L"DIFF3", tr("Diff Palette + Bayer1 Dither")); + m_mode.setItemUIName(L"NEW0", tr("New Pal Per Frame")); + m_mode.setItemUIName(L"NEW1", tr("New Pal Per Frame + Sierra Dither")); + m_mode.setItemUIName(L"NEW2", tr("New Pal Per Frame + Bayer2 Dither")); + m_mode.setItemUIName(L"NEW3", tr("New Pal Per Frame + Bayer1 Dither")); + m_mode.setItemUIName(L"NOPAL", tr("Opaque, Dither, 256 Colors Only")); + bind(m_scale); bind(m_looping); - bind(m_palette); + bind(m_mode); + bind(m_maxcolors); } void Tiio::GifWriterProperties::updateTranslation() { m_scale.setQStringName(tr("Scale")); m_looping.setQStringName(tr("Looping")); - m_palette.setQStringName(tr("Generate Palette")); + m_mode.setQStringName(tr("Mode")); + m_maxcolors.setQStringName(tr("Max Colors")); } // Tiio::Reader* Tiio::makeGifReader(){ return nullptr; } diff --git a/toonz/sources/image/ffmpeg/tiio_gif.h b/toonz/sources/image/ffmpeg/tiio_gif.h index 59587fa3..4ab0373a 100644 --- a/toonz/sources/image/ffmpeg/tiio_gif.h +++ b/toonz/sources/image/ffmpeg/tiio_gif.h @@ -37,8 +37,9 @@ private: int m_frameCount, m_lx, m_ly; // double m_fps; int m_scale; - bool m_looping = false; - bool m_palette = false; + bool m_looping = true; + int m_mode = 0; + int m_maxcolors = 256; }; //=========================================================== @@ -81,7 +82,9 @@ class GifWriterProperties : public TPropertyGroup { public: TIntProperty m_scale; TBoolProperty m_looping; - TBoolProperty m_palette; + TEnumProperty m_mode; + TIntProperty m_maxcolors; + GifWriterProperties(); void updateTranslation() override; diff --git a/toonz/sources/include/tproperty.h b/toonz/sources/include/tproperty.h index a3e57d2e..660a4378 100644 --- a/toonz/sources/include/tproperty.h +++ b/toonz/sources/include/tproperty.h @@ -398,7 +398,10 @@ public: void setValue(const std::wstring &value) { int idx = indexOf(value); - if (idx < 0) throw RangeError(); + // if (idx < 0) throw RangeError(); + if (idx < 0) + idx = 0; // Avoid exception if program's item list doesn't contain + // the selected item in scene file m_index = idx; } diff --git a/toonz/sources/toonz/dvwidgets.cpp b/toonz/sources/toonz/dvwidgets.cpp index 18d4cf25..770ec8a8 100644 --- a/toonz/sources/toonz/dvwidgets.cpp +++ b/toonz/sources/toonz/dvwidgets.cpp @@ -115,3 +115,26 @@ void PropertyIntField::onPropertyChanged() { TIntProperty *prop = dynamic_cast(m_property); if (prop) this->setValue(prop->getValue()); } + +//============================================================================= +// PropertyDoubleField +//----------------------------------------------------------------------------- + +PropertyDoubleField::PropertyDoubleField(QWidget *parent, TDoubleProperty *prop) + : DoubleField(parent), PropertyWidget(prop) { + connect(this, SIGNAL(valueChanged(bool)), this, SLOT(onValueChanged(bool))); +} + +//----------------------------------------------------------------------------- + +void PropertyDoubleField::onValueChanged(bool isDragging) { + TDoubleProperty *prop = dynamic_cast(m_property); + if (prop) prop->setValue(getValue()); +} + +//----------------------------------------------------------------------------- + +void PropertyDoubleField::onPropertyChanged() { + TDoubleProperty *prop = dynamic_cast(m_property); + if (prop) this->setValue(prop->getValue()); +} diff --git a/toonz/sources/toonz/dvwidgets.h b/toonz/sources/toonz/dvwidgets.h index 3bdfd15d..f8faabea 100644 --- a/toonz/sources/toonz/dvwidgets.h +++ b/toonz/sources/toonz/dvwidgets.h @@ -8,6 +8,7 @@ #include "tproperty.h" #include "toonzqt/intfield.h" +#include "toonzqt/doublefield.h" #include "toonzqt/lineedit.h" #include "toonzqt/checkbox.h" @@ -93,6 +94,21 @@ private: void onPropertyChanged() override; }; +//----------------------------------------------------------------------------- + +class PropertyDoubleField final : public DoubleField, public PropertyWidget { + Q_OBJECT + +public: + PropertyDoubleField(QWidget *parent, TDoubleProperty *prop); + +protected slots: + void onValueChanged(bool isDragging); + +private: + void onPropertyChanged() override; +}; + //----------------------------------------------------------------------------- } // namespace DVGui //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/formatsettingspopups.cpp b/toonz/sources/toonz/formatsettingspopups.cpp index 6c493bc6..ee9f7130 100644 --- a/toonz/sources/toonz/formatsettingspopups.cpp +++ b/toonz/sources/toonz/formatsettingspopups.cpp @@ -63,6 +63,8 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent, buildPropertyComboBox(i, m_props); else if (dynamic_cast(m_props->getProperty(i))) buildValueField(i, m_props); + else if (dynamic_cast(m_props->getProperty(i))) + buildDoubleField(i, m_props); else if (dynamic_cast(m_props->getProperty(i))) buildPropertyCheckBox(i, m_props); else if (dynamic_cast(m_props->getProperty(i))) @@ -88,6 +90,14 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent, } #endif + QPushButton *closeButton = new QPushButton(tr("Close")); + closeButton->setContentsMargins(4, 4, 4, 4); + closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + connect(closeButton, SIGNAL(released()), this, SLOT(close())); + + m_mainLayout->addWidget(closeButton, m_mainLayout->rowCount(), 0, 1, 2, + Qt::AlignCenter); + m_topLayout->addLayout(m_mainLayout, 1); } @@ -173,6 +183,28 @@ void FormatSettingsPopup::buildValueField(int index, TPropertyGroup *props) { //----------------------------------------------------------------------------- +void FormatSettingsPopup::buildDoubleField(int index, TPropertyGroup *props) { + TDoubleProperty *prop = (TDoubleProperty *)(props->getProperty(index)); + assert(prop); + + DVGui::PropertyDoubleField *v = new DVGui::PropertyDoubleField(this, prop); + m_widgets[prop->getName()] = v; + + int row = m_mainLayout->rowCount(); + m_mainLayout->addWidget(new QLabel(prop->getQStringName() + ":", this), row, + 0, Qt::AlignRight | Qt::AlignVCenter); + m_mainLayout->addWidget(v, row, 1); + // get value here - bug loses value if the range doesn't start with 0 + double value = prop->getValue(); + v->setValues(prop->getValue(), prop->getRange().first, + prop->getRange().second); + if (prop->getValue() != value) { + prop->setValue(value); + } +} + +//----------------------------------------------------------------------------- + void FormatSettingsPopup::buildPropertyCheckBox(int index, TPropertyGroup *props) { TBoolProperty *prop = (TBoolProperty *)(props->getProperty(index)); diff --git a/toonz/sources/toonz/formatsettingspopups.h b/toonz/sources/toonz/formatsettingspopups.h index 1fbfbdff..5e3bc27e 100644 --- a/toonz/sources/toonz/formatsettingspopups.h +++ b/toonz/sources/toonz/formatsettingspopups.h @@ -62,6 +62,7 @@ private: private: void buildPropertyComboBox(int index, TPropertyGroup *props); void buildValueField(int index, TPropertyGroup *props); + void buildDoubleField(int index, TPropertyGroup *props); void buildPropertyCheckBox(int index, TPropertyGroup *props); void buildPropertyLineEdit(int index, TPropertyGroup *props); void showEvent(QShowEvent *se) override; diff --git a/toonz/sources/toonzlib/outputproperties.cpp b/toonz/sources/toonzlib/outputproperties.cpp index 2b90dde2..dc046144 100644 --- a/toonz/sources/toonzlib/outputproperties.cpp +++ b/toonz/sources/toonzlib/outputproperties.cpp @@ -160,8 +160,13 @@ TPropertyGroup *TOutputProperties::getFileFormatProperties(std::string ext) { TPropertyGroup *ret = Tiio::makeWriterProperties(ext); m_formatProperties[ext] = ret; return ret; - } else - return it->second; + } else { + // Try to merge settings instead of overriding them + TPropertyGroup *ret = Tiio::makeWriterProperties(ext); + ret->setProperties(it->second); + m_formatProperties[ext] = ret; + return ret; + } } //-------------------------------------------------------------------