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 *)m_properties->getProperty("Looping");
|
||||
m_looping = looping->getValue();
|
||||
TBoolProperty *palette =
|
||||
(TBoolProperty *)m_properties->getProperty("Generate Palette");
|
||||
m_palette = palette->getValue();
|
||||
TEnumProperty *modeProp =
|
||||
dynamic_cast<TEnumProperty *>(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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -115,3 +115,26 @@ void PropertyIntField::onPropertyChanged() {
|
|||
TIntProperty *prop = dynamic_cast<TIntProperty *>(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<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 "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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -63,6 +63,8 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent,
|
|||
buildPropertyComboBox(i, m_props);
|
||||
else if (dynamic_cast<TIntProperty *>(m_props->getProperty(i)))
|
||||
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)))
|
||||
buildPropertyCheckBox(i, m_props);
|
||||
else if (dynamic_cast<TStringProperty *>(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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue