FFMPEG GIF Export enhancements
This commit is contained in:
parent
119de0256f
commit
3d2774a74d
8 changed files with 191 additions and 38 deletions
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue