turtletooth 04d8fd
turtletooth 04d8fd
#include "tsystem.h"
turtletooth 04d8fd
#include "tiio_gif.h"
turtletooth 04d8fd
#include "trasterimage.h"
turtletooth 04d8fd
#include "timageinfo.h"
Jeremy Bullock 2b9d8f
#include "toonz/stage.h"
turtletooth 04d8fd
#include <qstringlist></qstringlist>
turtletooth 04d8fd
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
//
turtletooth 04d8fd
//  TImageWriterGif
turtletooth 04d8fd
//
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
turtletooth 04d8fd
class TImageWriterGif : public TImageWriter {
turtletooth 04d8fd
public:
turtletooth 04d8fd
  int m_frameIndex;
turtletooth 04d8fd
turtletooth 04d8fd
  TImageWriterGif(const TFilePath &path, int frameIndex, TLevelWriterGif *lwg)
turtletooth 04d8fd
      : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) {
turtletooth 04d8fd
    m_lwg->addRef();
turtletooth 04d8fd
  }
turtletooth 04d8fd
  ~TImageWriterGif() { m_lwg->release(); }
turtletooth 04d8fd
turtletooth 04d8fd
  bool is64bitOutputSupported() override { return false; }
turtletooth 04d8fd
  void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); }
turtletooth 04d8fd
turtletooth 04d8fd
private:
turtletooth 04d8fd
  TLevelWriterGif *m_lwg;
turtletooth 04d8fd
};
turtletooth 04d8fd
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
//
turtletooth 04d8fd
//  TLevelWriterGif;
turtletooth 04d8fd
//
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
turtletooth 04d8fd
TLevelWriterGif::TLevelWriterGif(const TFilePath &path, TPropertyGroup *winfo)
turtletooth 04d8fd
    : TLevelWriter(path, winfo) {
turtletooth 04d8fd
  if (!m_properties) m_properties = new Tiio::GifWriterProperties();
shun-iwasawa 27b0cf
  std::string scale = m_properties->getProperty("Scale")->getValueAsString();
shun-iwasawa 27b0cf
  m_scale           = QString::fromStdString(scale).toInt();
turtletooth 04d8fd
  TBoolProperty *looping =
turtletooth 04d8fd
      (TBoolProperty *)m_properties->getProperty("Looping");
turtletooth 04d8fd
  m_looping = looping->getValue();
justburner e7c1a3
  TEnumProperty *modeProp =
justburner e7c1a3
      dynamic_cast<tenumproperty *="">(m_properties->getProperty("Mode"));</tenumproperty>
justburner e7c1a3
  m_mode = 0;
justburner e7c1a3
  if (modeProp) {
justburner e7c1a3
    m_mode = modeProp->getIndex();
justburner e7c1a3
  }
justburner e7c1a3
  std::string maxcolors =
justburner e7c1a3
      m_properties->getProperty("Max Colors")->getValueAsString();
justburner e7c1a3
  m_maxcolors = QString::fromStdString(maxcolors).toInt();
justburner e7c1a3
turtletooth 04d8fd
  ffmpegWriter = new Ffmpeg();
turtletooth 04d8fd
  ffmpegWriter->setPath(m_path);
turtletooth 04d8fd
  // m_frameCount = 0;
turtletooth 04d8fd
  if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TLevelWriterGif::~TLevelWriterGif() {
turtletooth 04d8fd
  QStringList preIArgs;
turtletooth 04d8fd
  QStringList postIArgs;
turtletooth 04d8fd
  QStringList palettePreIArgs;
turtletooth 04d8fd
  QStringList palettePostIArgs;
turtletooth 04d8fd
turtletooth 04d8fd
  int outLx = m_lx;
turtletooth 04d8fd
  int outLy = m_ly;
turtletooth 04d8fd
turtletooth 04d8fd
  // set scaling
turtletooth 04d8fd
  outLx = m_lx * m_scale / 100;
turtletooth 04d8fd
  outLy = m_ly * m_scale / 100;
justburner e7c1a3
  /*
shun-iwasawa 27b0cf
  // ffmpeg doesn't like resolutions that aren't divisible by 2.
turtletooth 04d8fd
  if (outLx % 2 != 0) outLx++;
turtletooth 04d8fd
  if (outLy % 2 != 0) outLy++;
justburner e7c1a3
  */
justburner e7c1a3
justburner e7c1a3
  double framerate = (m_frameRate < 1.0 ? 1.0 : m_frameRate);
justburner e7c1a3
justburner e7c1a3
  QString filters = "fps=" + QString::number(framerate) +
justburner e7c1a3
                    ",scale=" + QString::number(outLx) + ":" +
justburner e7c1a3
                    QString::number(outLy) + ":flags=lanczos";
justburner e7c1a3
justburner e7c1a3
  // 1 = "dither=sierra2_4a" is default
justburner e7c1a3
  const char *ditherConsts[4] = {"none", "sierra2_4a", "bayer:bayer_scale=2",
justburner e7c1a3
                                 "bayer:bayer_scale=1"};
justburner e7c1a3
justburner e7c1a3
  // Please be careful when moving items, logic AND 3 requires alignment
justburner e7c1a3
  switch (m_mode) {
justburner e7c1a3
  case 0:
justburner e7c1a3
  case 1:
justburner e7c1a3
  case 2:
justburner e7c1a3
  case 3:
justburner e7c1a3
    filters += ",split[o1][o2];[o1]palettegen";  // "stats_mode=full" is default
justburner e7c1a3
    if (m_maxcolors != 256) {
justburner e7c1a3
      filters += "=max_colors=" + QString::number(m_maxcolors);
justburner e7c1a3
    }
justburner e7c1a3
    filters += "[p];[o2]fifo[o3];[o3][p]paletteuse";
justburner e7c1a3
    if ((m_mode & 3) != 1) {
justburner e7c1a3
      filters += "=dither=" + QString(ditherConsts[m_mode & 3]);
justburner e7c1a3
    }
justburner e7c1a3
    break;
justburner e7c1a3
  case 4:
justburner e7c1a3
  case 5:
justburner e7c1a3
  case 6:
justburner e7c1a3
  case 7:
justburner e7c1a3
    filters += ",split[o1][o2];[o1]palettegen=stats_mode=diff";
justburner e7c1a3
    if (m_maxcolors != 256) {
justburner e7c1a3
      filters += ":max_colors=" + QString::number(m_maxcolors);
justburner e7c1a3
    }
justburner e7c1a3
    filters += "[p];[o2]fifo[o3];[o3][p]paletteuse";
justburner e7c1a3
    if ((m_mode & 3) != 1) {
justburner e7c1a3
      filters += "=dither=" + QString(ditherConsts[m_mode & 3]);
justburner e7c1a3
    }
justburner e7c1a3
    break;
justburner e7c1a3
  case 8:
justburner e7c1a3
  case 9:
justburner e7c1a3
  case 10:
justburner e7c1a3
  case 11:
justburner e7c1a3
    filters += ",split[o1][o2];[o1]palettegen=stats_mode=single";
justburner e7c1a3
    if (m_maxcolors != 256) {
justburner e7c1a3
      filters += ":max_colors=" + QString::number(m_maxcolors);
justburner e7c1a3
    }
justburner e7c1a3
    filters += "[p];[o2]fifo[o3];[o3][p]paletteuse=new=1";
justburner e7c1a3
    if ((m_mode & 3) != 1) {
justburner e7c1a3
      filters += ":dither=" + QString(ditherConsts[m_mode & 3]);
justburner e7c1a3
    }
justburner e7c1a3
    break;
justburner e7c1a3
  default:
justburner e7c1a3
    break;
turtletooth 04d8fd
  }
turtletooth 04d8fd
justburner 8ca956
  preIArgs << "-r";
justburner 8ca956
  preIArgs << QString::number(framerate);
turtletooth 04d8fd
  preIArgs << "-v";
turtletooth 04d8fd
  preIArgs << "warning";
justburner e7c1a3
  postIArgs << "-vf";
justburner e7c1a3
  postIArgs << filters;
justburner e7c1a3
  postIArgs << "-gifflags";
justburner e7c1a3
  postIArgs << "0";
turtletooth 04d8fd
turtletooth 04d8fd
  if (!m_looping) {
turtletooth 04d8fd
    postIArgs << "-loop";
turtletooth 04d8fd
    postIArgs << "-1";
turtletooth 04d8fd
  }
turtletooth 04d8fd
turtletooth 04d8fd
  std::string outPath = m_path.getQString().toStdString();
turtletooth 04d8fd
turtletooth 04d8fd
  ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true);
turtletooth 04d8fd
  ffmpegWriter->cleanUpFiles();
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TImageWriterP TLevelWriterGif::getFrameWriter(TFrameId fid) {
turtletooth 04d8fd
  // if (IOError != 0)
turtletooth 04d8fd
  //	throw TImageException(m_path, buildGifExceptionString(IOError));
shun-iwasawa ef0f8b
  if (!fid.getLetter().isEmpty()) return TImageWriterP(0);
turtletooth 04d8fd
  int index            = fid.getNumber();
turtletooth 04d8fd
  TImageWriterGif *iwg = new TImageWriterGif(m_path, index, this);
turtletooth 04d8fd
  return TImageWriterP(iwg);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
void TLevelWriterGif::setFrameRate(double fps) {
turtletooth 04d8fd
  // m_fps = fps;
turtletooth 04d8fd
  m_frameRate = fps;
turtletooth 04d8fd
  ffmpegWriter->setFrameRate(fps);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
void TLevelWriterGif::saveSoundTrack(TSoundTrack *st) { return; }
turtletooth 04d8fd
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
void TLevelWriterGif::save(const TImageP &img, int frameIndex) {
turtletooth 04d8fd
  TRasterImageP image(img);
turtletooth 04d8fd
  m_lx = image->getRaster()->getLx();
turtletooth 04d8fd
  m_ly = image->getRaster()->getLy();
turtletooth 04d8fd
  ffmpegWriter->createIntermediateImage(img, frameIndex);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
//
turtletooth 04d8fd
//  TImageReaderGif
turtletooth 04d8fd
//
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
turtletooth 04d8fd
class TImageReaderGif final : public TImageReader {
turtletooth 04d8fd
public:
turtletooth 04d8fd
  int m_frameIndex;
turtletooth 04d8fd
Jeremy Bullock 2b9d8f
  TImageReaderGif(const TFilePath &path, int index, TLevelReaderGif *lra,
Jeremy Bullock 2b9d8f
                  TImageInfo *info)
Jeremy Bullock 2b9d8f
      : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) {
turtletooth 04d8fd
    m_lra->addRef();
turtletooth 04d8fd
  }
turtletooth 04d8fd
  ~TImageReaderGif() { m_lra->release(); }
turtletooth 04d8fd
turtletooth 04d8fd
  TImageP load() override { return m_lra->load(m_frameIndex); }
turtletooth 04d8fd
  TDimension getSize() const { return m_lra->getSize(); }
turtletooth 04d8fd
  TRect getBBox() const { return TRect(); }
Jeremy Bullock 2b9d8f
  const TImageInfo *getImageInfo() const override { return m_info; }
turtletooth 04d8fd
turtletooth 04d8fd
private:
turtletooth 04d8fd
  TLevelReaderGif *m_lra;
Jeremy Bullock 2b9d8f
  TImageInfo *m_info;
turtletooth 04d8fd
turtletooth 04d8fd
  // not implemented
turtletooth 04d8fd
  TImageReaderGif(const TImageReaderGif &);
turtletooth 04d8fd
  TImageReaderGif &operator=(const TImageReaderGif &src);
turtletooth 04d8fd
};
turtletooth 04d8fd
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
//
turtletooth 04d8fd
//  TLevelReaderGif
turtletooth 04d8fd
//
turtletooth 04d8fd
//===========================================================
turtletooth 04d8fd
turtletooth 04d8fd
TLevelReaderGif::TLevelReaderGif(const TFilePath &path)
turtletooth 04d8fd
    : TLevelReader(path)
turtletooth 04d8fd
turtletooth 04d8fd
{
turtletooth 04d8fd
  ffmpegReader = new Ffmpeg();
turtletooth 04d8fd
  ffmpegReader->setPath(m_path);
turtletooth 04d8fd
  ffmpegReader->disablePrecompute();
turtletooth 04d8fd
  ffmpegFileInfo tempInfo = ffmpegReader->getInfo();
turtletooth 04d8fd
  double fps              = tempInfo.m_frameRate;
turtletooth 04d8fd
  m_frameCount            = tempInfo.m_frameCount;
turtletooth 04d8fd
  m_size                  = TDimension(tempInfo.m_lx, tempInfo.m_ly);
turtletooth 04d8fd
  m_lx                    = m_size.lx;
turtletooth 04d8fd
  m_ly                    = m_size.ly;
turtletooth 04d8fd
turtletooth 04d8fd
  // set values
turtletooth 04d8fd
  m_info                   = new TImageInfo();
turtletooth 04d8fd
  m_info->m_frameRate      = fps;
turtletooth 04d8fd
  m_info->m_lx             = m_lx;
turtletooth 04d8fd
  m_info->m_ly             = m_ly;
turtletooth 04d8fd
  m_info->m_bitsPerSample  = 8;
turtletooth 04d8fd
  m_info->m_samplePerPixel = 4;
Jeremy Bullock 2b9d8f
  m_info->m_dpix           = Stage::standardDpi;
Jeremy Bullock 2b9d8f
  m_info->m_dpiy           = Stage::standardDpi;
turtletooth 04d8fd
}
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TLevelReaderGif::~TLevelReaderGif() {
turtletooth 04d8fd
  // ffmpegReader->cleanUpFiles();
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TLevelP TLevelReaderGif::loadInfo() {
turtletooth 04d8fd
  if (m_frameCount == -1) return TLevelP();
turtletooth 04d8fd
  TLevelP level;
turtletooth 04d8fd
  for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP());
turtletooth 04d8fd
  return level;
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//-----------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TImageReaderP TLevelReaderGif::getFrameReader(TFrameId fid) {
turtletooth 04d8fd
  // if (IOError != 0)
turtletooth 04d8fd
  //	throw TImageException(m_path, buildAVIExceptionString(IOError));
shun-iwasawa ef0f8b
  if (!fid.getLetter().isEmpty()) return TImageReaderP(0);
Jeremy Bullock 2b9d8f
  int index            = fid.getNumber();
Jeremy Bullock 2b9d8f
  TImageReaderGif *irm = new TImageReaderGif(m_path, index, this, m_info);
turtletooth 04d8fd
  return TImageReaderP(irm);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
//------------------------------------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TDimension TLevelReaderGif::getSize() { return m_size; }
turtletooth 04d8fd
turtletooth 04d8fd
//------------------------------------------------
turtletooth 04d8fd
turtletooth 04d8fd
TImageP TLevelReaderGif::load(int frameIndex) {
manongjohn f2da01
  if (!ffmpegFramesCreated) {
manongjohn f2da01
    ffmpegReader->getFramesFromMovie();
manongjohn f2da01
    ffmpegFramesCreated = true;
manongjohn f2da01
  }
turtletooth 04d8fd
  return ffmpegReader->getImage(frameIndex);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
Tiio::GifWriterProperties::GifWriterProperties()
shun-iwasawa 27b0cf
    : m_scale("Scale", 1, 100, 100)
turtletooth 04d8fd
    , m_looping("Looping", true)
justburner 8ca956
    , m_palette("Generate Palette", true)
justburner e7c1a3
    , m_mode("Mode")
justburner e7c1a3
    , m_maxcolors("Max Colors", 2, 256, 256) {
justburner e7c1a3
justburner e7c1a3
  // Set values for mode
justburner e7c1a3
  m_mode.addValue(L"GLOBAL0");
justburner e7c1a3
  m_mode.addValue(L"GLOBAL1");
justburner e7c1a3
  m_mode.addValue(L"GLOBAL2");
justburner e7c1a3
  m_mode.addValue(L"GLOBAL3");
justburner e7c1a3
  m_mode.addValue(L"DIFF0");
justburner e7c1a3
  m_mode.addValue(L"DIFF1");
justburner e7c1a3
  m_mode.addValue(L"DIFF2");
justburner e7c1a3
  m_mode.addValue(L"DIFF3");
justburner e7c1a3
  m_mode.addValue(L"NEW0");
justburner e7c1a3
  m_mode.addValue(L"NEW1");
justburner e7c1a3
  m_mode.addValue(L"NEW2");
justburner e7c1a3
  m_mode.addValue(L"NEW3");
justburner e7c1a3
  m_mode.addValue(L"NOPAL");
justburner e7c1a3
justburner e7c1a3
  // Translate values
justburner e7c1a3
  m_mode.setItemUIName(L"GLOBAL0", tr("Global Palette"));
justburner e7c1a3
  m_mode.setItemUIName(L"GLOBAL1", tr("Global Palette + Sierra Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"GLOBAL2", tr("Global Palette + Bayer2 Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"GLOBAL3", tr("Global Palette + Bayer1 Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"DIFF0", tr("Diff Palette"));
justburner e7c1a3
  m_mode.setItemUIName(L"DIFF1", tr("Diff Palette + Sierra Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"DIFF2", tr("Diff Palette + Bayer2 Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"DIFF3", tr("Diff Palette + Bayer1 Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"NEW0", tr("New Pal Per Frame"));
justburner e7c1a3
  m_mode.setItemUIName(L"NEW1", tr("New Pal Per Frame + Sierra Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"NEW2", tr("New Pal Per Frame + Bayer2 Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"NEW3", tr("New Pal Per Frame + Bayer1 Dither"));
justburner e7c1a3
  m_mode.setItemUIName(L"NOPAL", tr("Opaque, Dither, 256 Colors Only"));
justburner e7c1a3
justburner 8ca956
  // Doesn't make Generate Palette property visible in the popup
justburner 8ca956
  m_palette.setVisible(false);
justburner 8ca956
turtletooth 04d8fd
  bind(m_scale);
turtletooth 04d8fd
  bind(m_looping);
justburner 8ca956
  bind(m_palette);
justburner e7c1a3
  bind(m_mode);
justburner e7c1a3
  bind(m_maxcolors);
turtletooth 04d8fd
}
turtletooth 04d8fd
shun-iwasawa e87e08
void Tiio::GifWriterProperties::updateTranslation() {
shun-iwasawa e87e08
  m_scale.setQStringName(tr("Scale"));
shun-iwasawa e87e08
  m_looping.setQStringName(tr("Looping"));
justburner e7c1a3
  m_mode.setQStringName(tr("Mode"));
justburner e7c1a3
  m_maxcolors.setQStringName(tr("Max Colors"));
shun-iwasawa e87e08
}
shun-iwasawa e87e08
turtletooth 04d8fd
// Tiio::Reader* Tiio::makeGifReader(){ return nullptr; }
turtletooth 04d8fd
// Tiio::Writer* Tiio::makeGifWriter(){ return nullptr; }