FFMPEG GIF Export enhancements

This commit is contained in:
justburner 2022-01-03 23:37:07 +00:00
parent 119de0256f
commit 3d2774a74d
8 changed files with 191 additions and 38 deletions

View file

@ -43,9 +43,16 @@ TLevelWriterGif::TLevelWriterGif(const TFilePath &path, TPropertyGroup *winfo)
TBoolProperty *looping = TBoolProperty *looping =
(TBoolProperty *)m_properties->getProperty("Looping"); (TBoolProperty *)m_properties->getProperty("Looping");
m_looping = looping->getValue(); m_looping = looping->getValue();
TBoolProperty *palette = TEnumProperty *modeProp =
(TBoolProperty *)m_properties->getProperty("Generate Palette"); dynamic_cast<TEnumProperty *>(m_properties->getProperty("Mode"));
m_palette = palette->getValue(); 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 = new Ffmpeg();
ffmpegWriter->setPath(m_path); ffmpegWriter->setPath(m_path);
// m_frameCount = 0; // m_frameCount = 0;
@ -66,42 +73,72 @@ TLevelWriterGif::~TLevelWriterGif() {
// set scaling // set scaling
outLx = m_lx * m_scale / 100; outLx = m_lx * m_scale / 100;
outLy = m_ly * m_scale / 100; outLy = m_ly * m_scale / 100;
/*
// ffmpeg doesn't like resolutions that aren't divisible by 2. // ffmpeg doesn't like resolutions that aren't divisible by 2.
if (outLx % 2 != 0) outLx++; if (outLx % 2 != 0) outLx++;
if (outLy % 2 != 0) outLy++; if (outLy % 2 != 0) outLy++;
*/
double framerate = (m_frameRate < 1.0 ? 1.0 : m_frameRate);
QString palette; QString filters = "fps=" + QString::number(framerate) +
QString filters = "scale=" + QString::number(outLx) + ":-1:flags=lanczos"; ",scale=" + QString::number(outLx) + ":" +
QString paletteFilters = filters + " [x]; [x][1:v] paletteuse"; QString::number(outLy) + ":flags=lanczos";
if (m_palette) {
palette = ffmpegWriter->getFfmpegCache().getQString() + "//" +
QString::fromStdString(m_path.getName()) + "palette.png";
palettePreIArgs << "-v";
palettePreIArgs << "warning";
palettePostIArgs << "-vf"; // 1 = "dither=sierra2_4a" is default
palettePostIArgs << filters + ",palettegen"; const char *ditherConsts[4] = {"none", "sierra2_4a", "bayer:bayer_scale=2",
palettePostIArgs << palette; "bayer:bayer_scale=1"};
// write the palette // Please be careful when moving items, logic AND 3 requires alignment
ffmpegWriter->runFfmpeg(palettePreIArgs, palettePostIArgs, false, true, switch (m_mode) {
true); case 0:
ffmpegWriter->addToCleanUp(palette); 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 << "-v";
preIArgs << "warning"; preIArgs << "warning";
preIArgs << "-r"; postIArgs << "-vf";
preIArgs << QString::number((m_frameRate < 1 ? 12.0 : m_frameRate)); postIArgs << filters;
if (m_palette) { postIArgs << "-gifflags";
postIArgs << "-i"; postIArgs << "0";
postIArgs << palette;
postIArgs << "-lavfi";
postIArgs << paletteFilters;
} else {
postIArgs << "-lavfi";
postIArgs << filters;
}
if (!m_looping) { if (!m_looping) {
postIArgs << "-loop"; postIArgs << "-loop";
@ -247,16 +284,49 @@ TImageP TLevelReaderGif::load(int frameIndex) {
Tiio::GifWriterProperties::GifWriterProperties() Tiio::GifWriterProperties::GifWriterProperties()
: m_scale("Scale", 1, 100, 100) : m_scale("Scale", 1, 100, 100)
, m_looping("Looping", true) , 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_scale);
bind(m_looping); bind(m_looping);
bind(m_palette); bind(m_mode);
bind(m_maxcolors);
} }
void Tiio::GifWriterProperties::updateTranslation() { void Tiio::GifWriterProperties::updateTranslation() {
m_scale.setQStringName(tr("Scale")); m_scale.setQStringName(tr("Scale"));
m_looping.setQStringName(tr("Looping")); 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; } // Tiio::Reader* Tiio::makeGifReader(){ return nullptr; }

View file

@ -37,8 +37,9 @@ private:
int m_frameCount, m_lx, m_ly; int m_frameCount, m_lx, m_ly;
// double m_fps; // double m_fps;
int m_scale; int m_scale;
bool m_looping = false; bool m_looping = true;
bool m_palette = false; int m_mode = 0;
int m_maxcolors = 256;
}; };
//=========================================================== //===========================================================
@ -81,7 +82,9 @@ class GifWriterProperties : public TPropertyGroup {
public: public:
TIntProperty m_scale; TIntProperty m_scale;
TBoolProperty m_looping; TBoolProperty m_looping;
TBoolProperty m_palette; TEnumProperty m_mode;
TIntProperty m_maxcolors;
GifWriterProperties(); GifWriterProperties();
void updateTranslation() override; void updateTranslation() override;

View file

@ -398,7 +398,10 @@ public:
void setValue(const std::wstring &value) { void setValue(const std::wstring &value) {
int idx = indexOf(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; m_index = idx;
} }

View file

@ -115,3 +115,26 @@ void PropertyIntField::onPropertyChanged() {
TIntProperty *prop = dynamic_cast<TIntProperty *>(m_property); TIntProperty *prop = dynamic_cast<TIntProperty *>(m_property);
if (prop) this->setValue(prop->getValue()); 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<TDoubleProperty *>(m_property);
if (prop) prop->setValue(getValue());
}
//-----------------------------------------------------------------------------
void PropertyDoubleField::onPropertyChanged() {
TDoubleProperty *prop = dynamic_cast<TDoubleProperty *>(m_property);
if (prop) this->setValue(prop->getValue());
}

View file

@ -8,6 +8,7 @@
#include "tproperty.h" #include "tproperty.h"
#include "toonzqt/intfield.h" #include "toonzqt/intfield.h"
#include "toonzqt/doublefield.h"
#include "toonzqt/lineedit.h" #include "toonzqt/lineedit.h"
#include "toonzqt/checkbox.h" #include "toonzqt/checkbox.h"
@ -93,6 +94,21 @@ private:
void onPropertyChanged() override; 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 } // namespace DVGui
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View file

@ -63,6 +63,8 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent,
buildPropertyComboBox(i, m_props); buildPropertyComboBox(i, m_props);
else if (dynamic_cast<TIntProperty *>(m_props->getProperty(i))) else if (dynamic_cast<TIntProperty *>(m_props->getProperty(i)))
buildValueField(i, m_props); buildValueField(i, m_props);
else if (dynamic_cast<TDoubleProperty *>(m_props->getProperty(i)))
buildDoubleField(i, m_props);
else if (dynamic_cast<TBoolProperty *>(m_props->getProperty(i))) else if (dynamic_cast<TBoolProperty *>(m_props->getProperty(i)))
buildPropertyCheckBox(i, m_props); buildPropertyCheckBox(i, m_props);
else if (dynamic_cast<TStringProperty *>(m_props->getProperty(i))) else if (dynamic_cast<TStringProperty *>(m_props->getProperty(i)))
@ -88,6 +90,14 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent,
} }
#endif #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); 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, void FormatSettingsPopup::buildPropertyCheckBox(int index,
TPropertyGroup *props) { TPropertyGroup *props) {
TBoolProperty *prop = (TBoolProperty *)(props->getProperty(index)); TBoolProperty *prop = (TBoolProperty *)(props->getProperty(index));

View file

@ -62,6 +62,7 @@ private:
private: private:
void buildPropertyComboBox(int index, TPropertyGroup *props); void buildPropertyComboBox(int index, TPropertyGroup *props);
void buildValueField(int index, TPropertyGroup *props); void buildValueField(int index, TPropertyGroup *props);
void buildDoubleField(int index, TPropertyGroup *props);
void buildPropertyCheckBox(int index, TPropertyGroup *props); void buildPropertyCheckBox(int index, TPropertyGroup *props);
void buildPropertyLineEdit(int index, TPropertyGroup *props); void buildPropertyLineEdit(int index, TPropertyGroup *props);
void showEvent(QShowEvent *se) override; void showEvent(QShowEvent *se) override;

View file

@ -160,8 +160,13 @@ TPropertyGroup *TOutputProperties::getFileFormatProperties(std::string ext) {
TPropertyGroup *ret = Tiio::makeWriterProperties(ext); TPropertyGroup *ret = Tiio::makeWriterProperties(ext);
m_formatProperties[ext] = ret; m_formatProperties[ext] = ret;
return ret; return ret;
} else } else {
return it->second; // Try to merge settings instead of overriding them
TPropertyGroup *ret = Tiio::makeWriterProperties(ext);
ret->setProperties(it->second);
m_formatProperties[ext] = ret;
return ret;
}
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------