2016-07-29 17:35:10 +12:00
|
|
|
|
|
|
|
#include "tsystem.h"
|
|
|
|
#include "tiio_gif.h"
|
|
|
|
#include "trasterimage.h"
|
|
|
|
#include "timageinfo.h"
|
2016-10-07 22:59:06 +13:00
|
|
|
#include "toonz/stage.h"
|
2016-07-29 17:35:10 +12:00
|
|
|
#include <QStringList>
|
|
|
|
|
|
|
|
//===========================================================
|
|
|
|
//
|
|
|
|
// TImageWriterGif
|
|
|
|
//
|
|
|
|
//===========================================================
|
|
|
|
|
|
|
|
class TImageWriterGif : public TImageWriter {
|
|
|
|
public:
|
|
|
|
int m_frameIndex;
|
|
|
|
|
|
|
|
TImageWriterGif(const TFilePath &path, int frameIndex, TLevelWriterGif *lwg)
|
|
|
|
: TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) {
|
|
|
|
m_lwg->addRef();
|
|
|
|
}
|
|
|
|
~TImageWriterGif() { m_lwg->release(); }
|
|
|
|
|
|
|
|
bool is64bitOutputSupported() override { return false; }
|
|
|
|
void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
TLevelWriterGif *m_lwg;
|
|
|
|
};
|
|
|
|
|
|
|
|
//===========================================================
|
|
|
|
//
|
|
|
|
// TLevelWriterGif;
|
|
|
|
//
|
|
|
|
//===========================================================
|
|
|
|
|
|
|
|
TLevelWriterGif::TLevelWriterGif(const TFilePath &path, TPropertyGroup *winfo)
|
|
|
|
: TLevelWriter(path, winfo) {
|
|
|
|
if (!m_properties) m_properties = new Tiio::GifWriterProperties();
|
2016-08-04 19:23:36 +12:00
|
|
|
std::string scale = m_properties->getProperty("Scale")->getValueAsString();
|
|
|
|
m_scale = QString::fromStdString(scale).toInt();
|
2016-07-29 17:35:10 +12:00
|
|
|
TBoolProperty *looping =
|
|
|
|
(TBoolProperty *)m_properties->getProperty("Looping");
|
|
|
|
m_looping = looping->getValue();
|
2022-01-29 02:03:41 +13:00
|
|
|
TBoolProperty *palette =
|
|
|
|
(TBoolProperty *)m_properties->getProperty("Generate Palette");
|
|
|
|
m_palette = palette->getValue();
|
2022-01-04 12:37:07 +13:00
|
|
|
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();
|
|
|
|
|
2016-07-29 17:35:10 +12:00
|
|
|
ffmpegWriter = new Ffmpeg();
|
|
|
|
ffmpegWriter->setPath(m_path);
|
|
|
|
// m_frameCount = 0;
|
|
|
|
if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
|
|
TLevelWriterGif::~TLevelWriterGif() {
|
|
|
|
QStringList preIArgs;
|
|
|
|
QStringList postIArgs;
|
|
|
|
QStringList palettePreIArgs;
|
|
|
|
QStringList palettePostIArgs;
|
|
|
|
|
|
|
|
int outLx = m_lx;
|
|
|
|
int outLy = m_ly;
|
|
|
|
|
|
|
|
// set scaling
|
|
|
|
outLx = m_lx * m_scale / 100;
|
|
|
|
outLy = m_ly * m_scale / 100;
|
2022-01-04 12:37:07 +13:00
|
|
|
/*
|
2016-08-04 19:23:36 +12:00
|
|
|
// ffmpeg doesn't like resolutions that aren't divisible by 2.
|
2016-07-29 17:35:10 +12:00
|
|
|
if (outLx % 2 != 0) outLx++;
|
|
|
|
if (outLy % 2 != 0) outLy++;
|
2022-01-04 12:37:07 +13:00
|
|
|
*/
|
2022-01-29 02:03:41 +13:00
|
|
|
QString palette;
|
2022-01-04 12:37:07 +13:00
|
|
|
double framerate = (m_frameRate < 1.0 ? 1.0 : m_frameRate);
|
|
|
|
|
|
|
|
QString filters = "fps=" + QString::number(framerate) +
|
|
|
|
",scale=" + QString::number(outLx) + ":" +
|
|
|
|
QString::number(outLy) + ":flags=lanczos";
|
|
|
|
|
|
|
|
// 1 = "dither=sierra2_4a" is default
|
|
|
|
const char *ditherConsts[4] = {"none", "sierra2_4a", "bayer:bayer_scale=2",
|
|
|
|
"bayer:bayer_scale=1"};
|
|
|
|
|
|
|
|
// 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;
|
2016-07-29 17:35:10 +12:00
|
|
|
}
|
2022-01-29 02:03:41 +13:00
|
|
|
#if 0
|
|
|
|
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";
|
|
|
|
|
|
|
|
palettePostIArgs << "-vf";
|
|
|
|
palettePostIArgs << filters + ",palettegen";
|
|
|
|
palettePostIArgs << palette;
|
|
|
|
|
|
|
|
// write the palette
|
|
|
|
ffmpegWriter->runFfmpeg(palettePreIArgs, palettePostIArgs, false, true,
|
|
|
|
true);
|
|
|
|
ffmpegWriter->addToCleanUp(palette);
|
|
|
|
}
|
|
|
|
#endif
|
2016-07-29 17:35:10 +12:00
|
|
|
|
2022-02-22 07:07:34 +13:00
|
|
|
preIArgs << "-r";
|
|
|
|
preIArgs << QString::number(framerate);
|
2016-07-29 17:35:10 +12:00
|
|
|
preIArgs << "-v";
|
|
|
|
preIArgs << "warning";
|
2022-01-29 02:03:41 +13:00
|
|
|
#if 0
|
|
|
|
if (m_palette) {
|
|
|
|
postIArgs << "-i";
|
|
|
|
postIArgs << palette;
|
|
|
|
postIArgs << "-lavfi";
|
|
|
|
postIArgs << paletteFilters;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
postIArgs << "-lavfi";
|
|
|
|
postIArgs << filters;
|
|
|
|
}
|
|
|
|
#endif
|
2022-01-04 12:37:07 +13:00
|
|
|
postIArgs << "-vf";
|
|
|
|
postIArgs << filters;
|
|
|
|
postIArgs << "-gifflags";
|
|
|
|
postIArgs << "0";
|
2016-07-29 17:35:10 +12:00
|
|
|
|
|
|
|
if (!m_looping) {
|
|
|
|
postIArgs << "-loop";
|
|
|
|
postIArgs << "-1";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string outPath = m_path.getQString().toStdString();
|
|
|
|
|
|
|
|
ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true);
|
|
|
|
ffmpegWriter->cleanUpFiles();
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
|
|
TImageWriterP TLevelWriterGif::getFrameWriter(TFrameId fid) {
|
|
|
|
// if (IOError != 0)
|
|
|
|
// throw TImageException(m_path, buildGifExceptionString(IOError));
|
2021-09-27 15:17:07 +13:00
|
|
|
if (!fid.getLetter().isEmpty()) return TImageWriterP(0);
|
2016-07-29 17:35:10 +12:00
|
|
|
int index = fid.getNumber();
|
|
|
|
TImageWriterGif *iwg = new TImageWriterGif(m_path, index, this);
|
|
|
|
return TImageWriterP(iwg);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriterGif::setFrameRate(double fps) {
|
|
|
|
// m_fps = fps;
|
|
|
|
m_frameRate = fps;
|
|
|
|
ffmpegWriter->setFrameRate(fps);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TLevelWriterGif::saveSoundTrack(TSoundTrack *st) { return; }
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
|
|
void TLevelWriterGif::save(const TImageP &img, int frameIndex) {
|
|
|
|
TRasterImageP image(img);
|
|
|
|
m_lx = image->getRaster()->getLx();
|
|
|
|
m_ly = image->getRaster()->getLy();
|
|
|
|
ffmpegWriter->createIntermediateImage(img, frameIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================
|
|
|
|
//
|
|
|
|
// TImageReaderGif
|
|
|
|
//
|
|
|
|
//===========================================================
|
|
|
|
|
|
|
|
class TImageReaderGif final : public TImageReader {
|
|
|
|
public:
|
|
|
|
int m_frameIndex;
|
|
|
|
|
2016-10-07 22:59:06 +13:00
|
|
|
TImageReaderGif(const TFilePath &path, int index, TLevelReaderGif *lra,
|
|
|
|
TImageInfo *info)
|
|
|
|
: TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) {
|
2016-07-29 17:35:10 +12:00
|
|
|
m_lra->addRef();
|
|
|
|
}
|
|
|
|
~TImageReaderGif() { m_lra->release(); }
|
|
|
|
|
|
|
|
TImageP load() override { return m_lra->load(m_frameIndex); }
|
|
|
|
TDimension getSize() const { return m_lra->getSize(); }
|
|
|
|
TRect getBBox() const { return TRect(); }
|
2016-10-07 22:59:06 +13:00
|
|
|
const TImageInfo *getImageInfo() const override { return m_info; }
|
2016-07-29 17:35:10 +12:00
|
|
|
|
|
|
|
private:
|
|
|
|
TLevelReaderGif *m_lra;
|
2016-10-07 22:59:06 +13:00
|
|
|
TImageInfo *m_info;
|
2016-07-29 17:35:10 +12:00
|
|
|
|
|
|
|
// not implemented
|
|
|
|
TImageReaderGif(const TImageReaderGif &);
|
|
|
|
TImageReaderGif &operator=(const TImageReaderGif &src);
|
|
|
|
};
|
|
|
|
|
|
|
|
//===========================================================
|
|
|
|
//
|
|
|
|
// TLevelReaderGif
|
|
|
|
//
|
|
|
|
//===========================================================
|
|
|
|
|
|
|
|
TLevelReaderGif::TLevelReaderGif(const TFilePath &path)
|
|
|
|
: TLevelReader(path)
|
|
|
|
|
|
|
|
{
|
|
|
|
ffmpegReader = new Ffmpeg();
|
|
|
|
ffmpegReader->setPath(m_path);
|
|
|
|
ffmpegReader->disablePrecompute();
|
|
|
|
ffmpegFileInfo tempInfo = ffmpegReader->getInfo();
|
|
|
|
double fps = tempInfo.m_frameRate;
|
|
|
|
m_frameCount = tempInfo.m_frameCount;
|
|
|
|
m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly);
|
|
|
|
m_lx = m_size.lx;
|
|
|
|
m_ly = m_size.ly;
|
|
|
|
|
|
|
|
// set values
|
|
|
|
m_info = new TImageInfo();
|
|
|
|
m_info->m_frameRate = fps;
|
|
|
|
m_info->m_lx = m_lx;
|
|
|
|
m_info->m_ly = m_ly;
|
|
|
|
m_info->m_bitsPerSample = 8;
|
|
|
|
m_info->m_samplePerPixel = 4;
|
2016-10-07 22:59:06 +13:00
|
|
|
m_info->m_dpix = Stage::standardDpi;
|
|
|
|
m_info->m_dpiy = Stage::standardDpi;
|
2016-07-29 17:35:10 +12:00
|
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
|
|
TLevelReaderGif::~TLevelReaderGif() {
|
|
|
|
// ffmpegReader->cleanUpFiles();
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
|
|
TLevelP TLevelReaderGif::loadInfo() {
|
|
|
|
if (m_frameCount == -1) return TLevelP();
|
|
|
|
TLevelP level;
|
|
|
|
for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP());
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
|
|
TImageReaderP TLevelReaderGif::getFrameReader(TFrameId fid) {
|
|
|
|
// if (IOError != 0)
|
|
|
|
// throw TImageException(m_path, buildAVIExceptionString(IOError));
|
2021-09-27 15:17:07 +13:00
|
|
|
if (!fid.getLetter().isEmpty()) return TImageReaderP(0);
|
2016-10-07 22:59:06 +13:00
|
|
|
int index = fid.getNumber();
|
|
|
|
TImageReaderGif *irm = new TImageReaderGif(m_path, index, this, m_info);
|
2016-07-29 17:35:10 +12:00
|
|
|
return TImageReaderP(irm);
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
TDimension TLevelReaderGif::getSize() { return m_size; }
|
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
TImageP TLevelReaderGif::load(int frameIndex) {
|
2019-11-14 18:07:58 +13:00
|
|
|
if (!ffmpegFramesCreated) {
|
|
|
|
ffmpegReader->getFramesFromMovie();
|
|
|
|
ffmpegFramesCreated = true;
|
|
|
|
}
|
2016-07-29 17:35:10 +12:00
|
|
|
return ffmpegReader->getImage(frameIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
Tiio::GifWriterProperties::GifWriterProperties()
|
2016-08-04 19:23:36 +12:00
|
|
|
: m_scale("Scale", 1, 100, 100)
|
2016-07-29 17:35:10 +12:00
|
|
|
, m_looping("Looping", true)
|
2022-01-29 02:03:41 +13:00
|
|
|
, m_palette("Generate Palette", true)
|
2022-01-04 12:37:07 +13:00
|
|
|
, 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"));
|
|
|
|
|
2022-02-22 07:07:34 +13:00
|
|
|
// Doesn't make Generate Palette property visible in the popup
|
|
|
|
m_palette.setVisible(false);
|
|
|
|
|
2016-07-29 17:35:10 +12:00
|
|
|
bind(m_scale);
|
|
|
|
bind(m_looping);
|
2022-01-29 02:03:41 +13:00
|
|
|
bind(m_palette);
|
2022-01-04 12:37:07 +13:00
|
|
|
bind(m_mode);
|
|
|
|
bind(m_maxcolors);
|
2016-07-29 17:35:10 +12:00
|
|
|
}
|
|
|
|
|
2018-06-04 15:18:43 +12:00
|
|
|
void Tiio::GifWriterProperties::updateTranslation() {
|
|
|
|
m_scale.setQStringName(tr("Scale"));
|
|
|
|
m_looping.setQStringName(tr("Looping"));
|
2022-01-29 02:03:41 +13:00
|
|
|
m_palette.setQStringName(tr("Generate Palette"));
|
2022-01-04 12:37:07 +13:00
|
|
|
m_mode.setQStringName(tr("Mode"));
|
|
|
|
m_maxcolors.setQStringName(tr("Max Colors"));
|
2018-06-04 15:18:43 +12:00
|
|
|
}
|
|
|
|
|
2016-07-29 17:35:10 +12:00
|
|
|
// Tiio::Reader* Tiio::makeGifReader(){ return nullptr; }
|
|
|
|
// Tiio::Writer* Tiio::makeGifWriter(){ return nullptr; }
|