Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "tsystem.h"
Toshihiro Shimizu 890ddd
#include "tstopwatch.h"
Toshihiro Shimizu 890ddd
#include "tthreadmessage.h"
Toshihiro Shimizu 890ddd
#include "timagecache.h"
Toshihiro Shimizu 890ddd
#include "tlevel_io.h"
Toshihiro Shimizu 890ddd
#include "trasterimage.h"
Toshihiro Shimizu 890ddd
#include "timageinfo.h"
Toshihiro Shimizu 890ddd
#include "trop.h"
Toshihiro Shimizu 890ddd
#include "tsop.h"
Toshihiro Shimizu 890ddd
shun-iwasawa 481b59
#include "tiio.h"
shun-iwasawa 481b59
Toshihiro Shimizu 890ddd
// TnzLib includes
Toshihiro Shimizu 890ddd
#include "toonz/toonzscene.h"
Toshihiro Shimizu 890ddd
#include "toonz/sceneproperties.h"
Toshihiro Shimizu 890ddd
#include "toonz/txsheet.h"
Toshihiro Shimizu 890ddd
#include "toonz/tcamera.h"
Toshihiro Shimizu 890ddd
#include "toonz/preferences.h"
Toshihiro Shimizu 890ddd
#include "toonz/trasterimageutils.h"
Toshihiro Shimizu 890ddd
#include "toonz/levelupdater.h"
Toshihiro Shimizu 890ddd
#include "toutputproperties.h"
shun-iwasawa cd4694
#include "toonz/boardsettings.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// tcg includes
Toshihiro Shimizu 890ddd
#include "tcg/tcg_macros.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Qt includes
Toshihiro Shimizu 890ddd
#include <qcoreapplication></qcoreapplication>
justburner 69bbaf
#include <qtimer></qtimer>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonz/movierenderer.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//**************************************************************************
Toshihiro Shimizu 890ddd
//    Local Namespace  stuff
Toshihiro Shimizu 890ddd
//**************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
int RenderSessionId = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void addMark(const TRasterP &mark, TRasterImageP img) {
Shinya Kitaoka 120a6e
  TRasterP raster = img->getRaster();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (raster->getLx() >= mark->getLx() && raster->getLy() >= mark->getLy()) {
Shinya Kitaoka 120a6e
    TRasterP ras = raster->clone();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int borderx = troundp(0.035 * (ras->getLx() - mark->getLx()));
Shinya Kitaoka 120a6e
    int bordery = troundp(0.035 * (ras->getLy() - mark->getLy()));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TRect rect = TRect(borderx, bordery, borderx + mark->getLx() - 1,
Shinya Kitaoka 120a6e
                       bordery + mark->getLy() - 1);
Shinya Kitaoka 120a6e
    TRop::over(ras->extract(rect), mark);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    img->setRaster(ras);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void getRange(ToonzScene *scene, bool isPreview, int &from, int &to) {
Shinya Kitaoka 120a6e
  TSceneProperties *sprop = scene->getProperties();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int step;
Shinya Kitaoka 120a6e
  if (isPreview)
Shinya Kitaoka 120a6e
    sprop->getPreviewProperties()->getRange(from, to, step);
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    sprop->getOutputProperties()->getRange(from, to, step);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (to < 0) {
Shinya Kitaoka 120a6e
    TXsheet *xs = scene->getXsheet();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // NOTE: Use of numeric_limits::min is justified since the type is
Shinya Kitaoka 120a6e
    // *INTERGRAL*.
Shinya Kitaoka 120a6e
    from = (std::numeric_limits<int>::max)(),</int>
Shinya Kitaoka 120a6e
    to   = (std::numeric_limits<int>::min)();</int>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    for (int k = 0; k < xs->getColumnCount(); ++k) {
Shinya Kitaoka 120a6e
      int r0, r1;
Shinya Kitaoka 120a6e
      xs->getCellRange(k, r0, r1);
Toshihiro Shimizu 890ddd
shun-iwasawa 481b59
      TXshColumn *col         = xs->getColumn(k);
JPUser1 c801ab
      TXshSoundColumn *sndCol = col ? col->getSoundColumn() : 0;
JPUser1 c801ab
shun-iwasawa 481b59
      if (sndCol) r0 = 0;
Shinya Kitaoka 120a6e
      from = std::min(from, r0), to = std::max(to, r1);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
QString getPreviewName(unsigned long renderSessionId) {
Shinya Kitaoka 120a6e
  return "previewed" + QString::number(renderSessionId) + ".noext";
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//**************************************************************************
Toshihiro Shimizu 890ddd
//    MovieRenderer::Imp  definition
Toshihiro Shimizu 890ddd
//**************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class MovieRenderer::Imp final : public TRenderPort, public TSmartObject {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  ToonzScene *m_scene;
Shinya Kitaoka 120a6e
  TRenderer m_renderer;
Shinya Kitaoka 120a6e
  TFilePath m_fp;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TRenderSettings m_renderSettings;
Shinya Kitaoka 120a6e
  TDimension m_frameSize;
Shinya Kitaoka 120a6e
  double m_xDpi, m_yDpi;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  std::set<movierenderer::listener *=""> m_listeners;</movierenderer::listener>
Shinya Kitaoka 2a7129
  std::unique_ptr<levelupdater> m_levelUpdaterA, m_levelUpdaterB;</levelupdater>
Shinya Kitaoka 120a6e
  TSoundTrackP m_st;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  std::map<double, std::pair<trasterp,="" trasterp="">> m_toBeSaved;</double,>
Shinya Kitaoka 120a6e
  std::vector<std::pair<double, tfxpair="">> m_framesToBeRendered;</std::pair<double,>
Shinya Kitaoka 120a6e
  std::string m_renderCacheId;
shun-iwasawa 443318
  /*--- When caching the same raster, gamma only the first one and use the
shun-iwasawa 443318
  result in subsequent frames
Shinya Kitaoka 120a6e
  ---*/
Shinya Kitaoka 120a6e
  std::map<double, bool=""> m_toBeAppliedGamma;</double,>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TThread::Mutex m_mutex;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int m_renderSessionId;
Shinya Kitaoka 120a6e
  long m_whiteSample;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int m_nextFrameIdxToSave;
Shinya Kitaoka 120a6e
  int m_savingThreadsCount;
Shinya Kitaoka 120a6e
  bool m_firstCompletedRaster;
Shinya Kitaoka 120a6e
  bool m_failure;
Shinya Kitaoka 120a6e
  bool m_cacheResults;
Shinya Kitaoka 120a6e
  bool m_preview;
Shinya Kitaoka 120a6e
  bool m_movieType;
justburner 69bbaf
  bool m_seqRequired;
justburner 69bbaf
  bool m_waitAfterFinish;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  Imp(ToonzScene *scene, const TFilePath &moviePath, int threadCount,
Shinya Kitaoka 120a6e
      bool cacheResults);
Shinya Kitaoka 120a6e
  ~Imp();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // TRenderPort methods
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void onRenderRasterCompleted(const RenderData &renderData) override;
Shinya Kitaoka 473e70
  void onRenderFailure(const RenderData &renderData, TException &e) override;
Toshihiro Shimizu 890ddd
shun-iwasawa 443318
  /*-- Do not update m_overallRenderedRegion on cancel --*/
Shinya Kitaoka 473e70
  void onRenderFinished(bool isCanceled = false) override;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void doRenderRasterCompleted(const RenderData &renderData);
Shinya Kitaoka 120a6e
  void doPreviewRasterCompleted(const RenderData &renderData);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Helper methods
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void prepareForStart();
shun-iwasawa a38488
  void addSoundtrack(int r0, int r1, double fps, int boardDuration = 0);
shun-iwasawa 481b59
shun-iwasawa 481b59
  // writeInLinearColorSpace : Whether the format will save image in linear
shun-iwasawa 481b59
  // color space. (true only in EXR fromat) writingGamma : Color space gamma to
shun-iwasawa 481b59
  // be used for saving in the file ("Color Space Gamma" property in EXR format)
shun-iwasawa 481b59
  // renderingGamma : Color space gamma used on rendering ( "Color Space Gamma"
shun-iwasawa 481b59
  // value in the Render Settings )
Shinya Kitaoka 120a6e
  void postProcessImage(const TRasterImageP &img, bool has64bitOutputSupport,
shun-iwasawa 481b59
                        bool writeInLinearColorSpace, bool isFirstTime,
shun-iwasawa 481b59
                        double writingGamma, double renderingGamma,
Shinya Kitaoka 120a6e
                        const TRasterP &mark, int frame);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  //! Saves the specified rasters at the specified time; returns whether the
Shinya Kitaoka 120a6e
  //! frames were successfully saved, and
Shinya Kitaoka 120a6e
  //! the associated time-adjusted level frame.
Shinya Kitaoka 120a6e
  std::pair<bool, int=""> saveFrame(double frame,</bool,>
Shinya Kitaoka 120a6e
                                 const std::pair<trasterp, trasterp=""> &rasters);</trasterp,>
Shinya Kitaoka 120a6e
  std::string getRenderCacheId();
shun-iwasawa cd4694
shun-iwasawa a38488
  // returns board duration in frame
shun-iwasawa a38488
  int addBoard();
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
MovieRenderer::Imp::Imp(ToonzScene *scene, const TFilePath &moviePath,
Shinya Kitaoka 120a6e
                        int threadCount, bool cacheResults)
Shinya Kitaoka 120a6e
    : m_scene(scene)
Shinya Kitaoka 120a6e
    , m_renderer(threadCount)
Shinya Kitaoka 120a6e
    , m_fp(moviePath)
Shinya Kitaoka 120a6e
    , m_frameSize(scene->getCurrentCamera()->getRes())
Shinya Kitaoka 120a6e
    , m_xDpi(72)
Shinya Kitaoka 120a6e
    , m_yDpi(72)
Shinya Kitaoka 120a6e
    , m_renderSessionId(RenderSessionId++)
Shinya Kitaoka 120a6e
    , m_nextFrameIdxToSave(0)
Shinya Kitaoka 120a6e
    , m_savingThreadsCount(0)
Shinya Kitaoka 120a6e
    , m_whiteSample(0)
Shinya Kitaoka 120a6e
    , m_firstCompletedRaster(
Shinya Kitaoka 120a6e
          true)         //< I know, sounds weird - it's just set to false
Shinya Kitaoka 120a6e
    , m_failure(false)  //  AFTER the first completed raster gets processed
Shinya Kitaoka 120a6e
    , m_cacheResults(cacheResults)
Shinya Kitaoka 120a6e
    , m_preview(moviePath.isEmpty())
justburner 69bbaf
    , m_movieType(isMovieType(moviePath))
justburner 69bbaf
    , m_seqRequired(isSequencialRequired(moviePath)) {
Shinya Kitaoka 120a6e
  m_renderCacheId =
Shinya Kitaoka 120a6e
      m_fp.withName(m_fp.getName() + "#RENDERID" +
Shinya Kitaoka 120a6e
                    QString::number(m_renderSessionId).toStdString())
Shinya Kitaoka 120a6e
          .getLevelName();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_renderer.addPort(this);
justburner 69bbaf
  m_waitAfterFinish = m_movieType && !m_seqRequired && threadCount > 1;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
MovieRenderer::Imp::~Imp() {
Shinya Kitaoka 120a6e
  m_renderer.removePort(this);  // Please, note: a TRenderer instance is
Shinya Kitaoka 120a6e
                                // currently a shared-pointer-like
Shinya Kitaoka 120a6e
}  // object to a private worker. *That* object may outlive the TRenderer
Shinya Kitaoka 120a6e
   // instance.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::prepareForStart() {
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static void eraseUncompatibleExistingLevel(
Shinya Kitaoka 120a6e
        const TFilePath &fp, const TDimension &imageSize)  // nothrow
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      assert(!fp.isEmpty());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (TSystem::doesExistFileOrLevel(fp)) {
Shinya Kitaoka 120a6e
        bool remove = false;
Shinya Kitaoka 120a6e
        // In case the raster specifics are different from those of a currently
Shinya Kitaoka 120a6e
        // existing movie, erase it
Shinya Kitaoka 120a6e
        try {
Jeremy Bullock 55b5db
          if (fp.isFfmpegType()) {
Jeremy Bullock 55b5db
            TSystem::removeFileOrLevel(fp);
Jeremy Bullock 55b5db
          } else {
Jeremy Bullock 55b5db
            TLevelReaderP lr(fp);
Jeremy Bullock 55b5db
            lr->loadInfo();
Jeremy Bullock 55b5db
Jeremy Bullock 55b5db
            const TImageInfo *info = lr->getImageInfo();
Jeremy Bullock 55b5db
            if (!info || info->m_lx != imageSize.lx ||
Jeremy Bullock 55b5db
                info->m_ly != imageSize.ly)
Jeremy Bullock 55b5db
              TSystem::removeFileOrLevel(fp);  // nothrow
Jeremy Bullock 55b5db
          }
Shinya Kitaoka 120a6e
        } catch (...) {
Shinya Kitaoka 120a6e
          // Same if the level could not be read/opened
Shinya Kitaoka 120a6e
          TSystem::removeFileOrLevel(fp);  // nothrow
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // NOTE: The level removal procedure could still fail.
Shinya Kitaoka 120a6e
        // In this case, no signaling takes place. The level readers will throw
Shinya Kitaoka 120a6e
        // when the time to write on the file comes, leading to a render
Shinya Kitaoka 120a6e
        // failure.
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  };
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TOutputProperties *oprop = m_scene->getProperties()->getOutputProperties();
Shinya Kitaoka 120a6e
  double frameRate         = (double)oprop->getFrameRate();
Shinya Kitaoka 120a6e
shun-iwasawa 443318
  /*-- stretch of the Frame rate --*/
Shinya Kitaoka 120a6e
  double stretchFactor = oprop->getRenderSettings().m_timeStretchTo /
Shinya Kitaoka 120a6e
                         oprop->getRenderSettings().m_timeStretchFrom;
Shinya Kitaoka 120a6e
  frameRate *= stretchFactor;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Get the shrink
Shinya Kitaoka 120a6e
  int shrinkX = m_renderSettings.m_shrinkX,
Shinya Kitaoka 120a6e
      shrinkY = m_renderSettings.m_shrinkY;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the render area
Shinya Kitaoka 120a6e
  TPointD cameraPos(-0.5 * m_frameSize.lx, -0.5 * m_frameSize.ly);
Shinya Kitaoka 120a6e
  TDimensionD cameraRes(double(m_frameSize.lx) / shrinkX,
Shinya Kitaoka 120a6e
                        double(m_frameSize.ly) / shrinkY);
Shinya Kitaoka 120a6e
  TDimension cameraResI(cameraRes.lx, cameraRes.ly);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TRectD renderArea(cameraPos.x, cameraPos.y, cameraPos.x + cameraRes.lx,
Shinya Kitaoka 120a6e
                    cameraPos.y + cameraRes.ly);
Shinya Kitaoka 120a6e
  setRenderArea(renderArea);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!m_fp.isEmpty()) {
Shinya Kitaoka 120a6e
    try  // Construction of a LevelUpdater may throw (well, almost ANY operation
Shinya Kitaoka 120a6e
         // on a LevelUpdater
Shinya Kitaoka 120a6e
    {    // could throw). But due to backward compatibility this function is
Shinya Kitaoka 120a6e
         // assumed to be non-throwing.
Shinya Kitaoka 120a6e
      if (!m_renderSettings.m_stereoscopic) {
Shinya Kitaoka 120a6e
        locals::eraseUncompatibleExistingLevel(m_fp, cameraResI);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        m_levelUpdaterA.reset(new LevelUpdater(
shun-iwasawa fc0d80
            m_fp, oprop->getFileFormatProperties(m_fp.getType()),
shun-iwasawa fc0d80
            oprop->formatTemplateFId()));
Shinya Kitaoka 120a6e
        m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate);
justburner 64e039
        m_fp = m_levelUpdaterA->getLevelWriter()->getFilePath();
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        TFilePath leftFp  = m_fp.withName(m_fp.getName() + "_l");
Shinya Kitaoka 120a6e
        TFilePath rightFp = m_fp.withName(m_fp.getName() + "_r");
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        locals::eraseUncompatibleExistingLevel(leftFp, cameraResI);
Shinya Kitaoka 120a6e
        locals::eraseUncompatibleExistingLevel(rightFp, cameraResI);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        m_levelUpdaterA.reset(new LevelUpdater(
shun-iwasawa fc0d80
            leftFp, oprop->getFileFormatProperties(leftFp.getType()),
shun-iwasawa fc0d80
            oprop->formatTemplateFId()));
Shinya Kitaoka 120a6e
        m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate);
justburner 64e039
        leftFp = m_levelUpdaterA->getLevelWriter()->getFilePath();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        m_levelUpdaterB.reset(new LevelUpdater(
shun-iwasawa fc0d80
            rightFp, oprop->getFileFormatProperties(rightFp.getType()),
shun-iwasawa fc0d80
            oprop->formatTemplateFId()));
Shinya Kitaoka 120a6e
        m_levelUpdaterB->getLevelWriter()->setFrameRate(frameRate);
justburner 64e039
        rightFp = m_levelUpdaterB->getLevelWriter()->getFilePath();
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    } catch (...) {
Shinya Kitaoka 120a6e
      // If we get here, it's because one of the LevelUpdaters could not be
Shinya Kitaoka 120a6e
      // created. So, let's say
Shinya Kitaoka 120a6e
      // that if one could not be created, then ALL OF THEM couldn't (ie saving
Shinya Kitaoka 120a6e
      // is not possible at all).
Shinya Kitaoka 120a6e
      m_levelUpdaterA.reset();
Shinya Kitaoka 120a6e
      m_levelUpdaterB.reset();
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
shun-iwasawa a38488
void MovieRenderer::Imp::addSoundtrack(int r0, int r1, double fps,
shun-iwasawa a38488
                                       int boardDuration) {
shun-iwasawa 481b59
  TCG_ASSERT(r0 <= r1, return);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TXsheet::SoundProperties *prop =
Shinya Kitaoka 120a6e
      new TXsheet::SoundProperties();  // Ownership will be surrendered ...
Shinya Kitaoka 120a6e
  prop->m_frameRate = fps;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TSoundTrack *snd = m_scene->getXsheet()->makeSound(prop);  // ... here
Shinya Kitaoka 120a6e
  if (!snd) {
Shinya Kitaoka 120a6e
    // No soundtrack
Shinya Kitaoka 120a6e
    m_whiteSample = (r1 - r0 + 1) * 918;  // 918?? wtf... I don't think it has
Shinya Kitaoka 120a6e
    return;  // any arcane meaning - but i'm not touching it.
Shinya Kitaoka 120a6e
  }          // My impression would be that... no sound implies
Shinya Kitaoka 120a6e
             // no access to m_whiteSample, no?
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double samplePerFrame = snd->getSampleRate() / fps;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Extract the useful part of scene soundtrack
Shinya Kitaoka 120a6e
  TSoundTrackP snd1 = snd->extract((TINT32)(r0 * samplePerFrame),
Shinya Kitaoka 120a6e
                                   (TINT32)(r1 * samplePerFrame));
Shinya Kitaoka 120a6e
  assert(!m_st);
Shinya Kitaoka 120a6e
  if (!m_st) {
Shinya Kitaoka 120a6e
    // First, add white sound before the 'from' instant
Shinya Kitaoka 120a6e
    m_st          = TSoundTrack::create(snd1->getFormat(), m_whiteSample);
Shinya Kitaoka 120a6e
    m_whiteSample = 0;  // Why?  Probably being pedantic here... I guess
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Then, add the rest
Shinya Kitaoka 120a6e
  TINT32 fromSample = m_st->getSampleCount();
Shinya Kitaoka 120a6e
  TINT32 numSample =
Shinya Kitaoka 120a6e
      std::max(TINT32((r1 - r0 + 1) * samplePerFrame), snd1->getSampleCount());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_st = TSop::insertBlank(m_st, fromSample, numSample + m_whiteSample);
Shinya Kitaoka 120a6e
  m_st->copy(snd1, TINT32(fromSample + m_whiteSample));
Shinya Kitaoka 120a6e
shun-iwasawa a38488
  // insert blank sound for clapperboard
shun-iwasawa a38488
  if (boardDuration > 0)
shun-iwasawa a38488
    m_st = TSop::insertBlank(m_st, 0, TINT32(boardDuration * samplePerFrame));
shun-iwasawa a38488
Shinya Kitaoka 120a6e
  m_whiteSample = 0;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::onRenderRasterCompleted(const RenderData &renderData) {
Shinya Kitaoka 120a6e
  if (m_preview)
Shinya Kitaoka 120a6e
    doPreviewRasterCompleted(renderData);
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    doRenderRasterCompleted(renderData);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::postProcessImage(const TRasterImageP &img,
Shinya Kitaoka 120a6e
                                          bool has64bitOutputSupport,
shun-iwasawa 481b59
                                          bool writeInLinearColorSpace,
shun-iwasawa 481b59
                                          bool isFirstTime, double writingGamma,
shun-iwasawa 481b59
                                          double renderingGamma,
Shinya Kitaoka 120a6e
                                          const TRasterP &mark, int frame) {
Shinya Kitaoka 120a6e
  img->setDpi(m_xDpi, m_yDpi);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (img->getRaster()->getPixelSize() == 8 && !has64bitOutputSupport) {
Shinya Kitaoka 120a6e
    TRaster32P aux(img->getRaster()->getLx(), img->getRaster()->getLy());
Shinya Kitaoka 120a6e
    TRop::convert(aux, img->getRaster());
Shinya Kitaoka 120a6e
    img->setRaster(aux);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
shun-iwasawa 481b59
  // raster will be converted to linear before saving in exr format
shun-iwasawa 481b59
  if (isFirstTime) {
shun-iwasawa 481b59
    if (img->getRaster()->isLinear()) {
shun-iwasawa 481b59
      if (!writeInLinearColorSpace)
shun-iwasawa 481b59
        TRop::tosRGB(img->getRaster(), renderingGamma);
shun-iwasawa 481b59
      // write in linear color space, but with different gamma
shun-iwasawa 481b59
      else if (!areAlmostEqual(renderingGamma, writingGamma)) {
shun-iwasawa 481b59
        double gammaAdjust = writingGamma / renderingGamma;
shun-iwasawa 481b59
        // temporarily release the linear flag in order to use toLinearRGB
shun-iwasawa 481b59
        img->getRaster()->setLinear(false);
shun-iwasawa 481b59
        TRop::toLinearRGB(img->getRaster(), gammaAdjust);
shun-iwasawa 481b59
      }
shun-iwasawa 481b59
    } else if (!img->getRaster()->isLinear() && writeInLinearColorSpace) {
shun-iwasawa 481b59
      TRop::toLinearRGB(img->getRaster(), writingGamma);
shun-iwasawa 481b59
    }
shun-iwasawa 481b59
  }
shun-iwasawa 481b59
Shinya Kitaoka 120a6e
  if (mark) addMark(mark, img);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (Preferences::instance()->isSceneNumberingEnabled())
Shinya Kitaoka 120a6e
    TRasterImageUtils::addGlobalNumbering(img, m_scene->getSceneName(), frame);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
std::pair<bool, int=""> MovieRenderer::Imp::saveFrame(</bool,>
Shinya Kitaoka 120a6e
    double frame, const std::pair<trasterp, trasterp=""> &rasters) {</trasterp,>
Shinya Kitaoka 120a6e
  bool success = false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the frame number to write to
Shinya Kitaoka 120a6e
  double stretchFac = double(m_renderSettings.m_timeStretchTo) /
Shinya Kitaoka 120a6e
                      m_renderSettings.m_timeStretchFrom;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int fr = (stretchFac != 1) ? tround(frame * stretchFac) : int(frame);
shun-iwasawa cd4694
shun-iwasawa cd4694
  int boardDuration = 0;
shun-iwasawa cd4694
  if (m_movieType) {
shun-iwasawa cd4694
    BoardSettings *bs =
shun-iwasawa cd4694
        m_scene->getProperties()->getOutputProperties()->getBoardSettings();
shun-iwasawa cd4694
    boardDuration = (bs->isActive()) ? bs->getDuration() : 0;
shun-iwasawa cd4694
  }
shun-iwasawa cd4694
shun-iwasawa cd4694
  TFrameId fid(fr + 1 + boardDuration);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_levelUpdaterA.get()) {
Shinya Kitaoka 120a6e
    assert(m_levelUpdaterB.get() || !rasters.second);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Analyze writer
shun-iwasawa 481b59
    bool has64bitOutputSupport   = false;
shun-iwasawa 481b59
    bool writeInLinearColorSpace = false;
shun-iwasawa 481b59
    double writingGamma          = 2.2;
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      if (TImageWriterP writerA =
shun-iwasawa 481b59
              m_levelUpdaterA->getLevelWriter()->getFrameWriter(fid)) {
Shinya Kitaoka 120a6e
        has64bitOutputSupport = writerA->is64bitOutputSupported();
Shinya Kitaoka 120a6e
shun-iwasawa 481b59
        const std::string &type = toLower(m_fp.getType());
shun-iwasawa 481b59
        Tiio::Writer *writer    = Tiio::makeWriter(type);
shun-iwasawa 481b59
        writeInLinearColorSpace = writer && writer->writeInLinearColorSpace();
shun-iwasawa 481b59
        if (writeInLinearColorSpace) {
shun-iwasawa 481b59
          TDoubleProperty *gammaProp =
shun-iwasawa 481b59
              (TDoubleProperty *)(m_levelUpdaterA->getLevelWriter()
shun-iwasawa 481b59
                                      ->getProperties()
shun-iwasawa 481b59
                                      ->getProperty("Color Space Gamma"));
shun-iwasawa 481b59
          if (gammaProp) writingGamma = gammaProp->getValue();
shun-iwasawa 481b59
        }
shun-iwasawa 481b59
      }
shun-iwasawa 481b59
Shinya Kitaoka 120a6e
      // NOTE: If the writer could not be retrieved, the updater will throw.
luzpaz 27707d
      // Failure will be caught then.
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Prepare the images to be flushed
Shinya Kitaoka 120a6e
    TRasterP rasterA = rasters.first, rasterB = rasters.second;
Shinya Kitaoka 120a6e
    assert(rasterA);
Shinya Kitaoka 120a6e
shun-iwasawa 443318
    /*--- When caching the same raster, gamma only the first one and use the
shun-iwasawa 443318
result in subsequent frames
Shinya Kitaoka 120a6e
---*/
Shinya Kitaoka 120a6e
    if (m_renderSettings.m_gamma != 1.0 && m_toBeAppliedGamma[frame]) {
Shinya Kitaoka 120a6e
      TRop::gammaCorrect(rasterA, m_renderSettings.m_gamma);
Shinya Kitaoka 120a6e
      if (rasterB) TRop::gammaCorrect(rasterB, m_renderSettings.m_gamma);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Flush images
Shinya Kitaoka 120a6e
    try {
Shinya Kitaoka 120a6e
      TRasterImageP imgA(rasterA);
shun-iwasawa 481b59
      postProcessImage(imgA, has64bitOutputSupport, writeInLinearColorSpace,
shun-iwasawa 481b59
                       m_toBeAppliedGamma[frame], writingGamma,
shun-iwasawa 481b59
                       m_renderSettings.m_colorSpaceGamma,
shun-iwasawa 481b59
                       m_renderSettings.m_mark, fid.getNumber());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      m_levelUpdaterA->update(fid, imgA);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (rasterB) {
Shinya Kitaoka 120a6e
        TRasterImageP imgB(rasterB);
shun-iwasawa 481b59
        postProcessImage(imgB, has64bitOutputSupport, writeInLinearColorSpace,
shun-iwasawa 481b59
                         m_toBeAppliedGamma[frame], writingGamma,
shun-iwasawa 481b59
                         m_renderSettings.m_colorSpaceGamma,
shun-iwasawa 481b59
                         m_renderSettings.m_mark, fid.getNumber());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        m_levelUpdaterB->update(fid, imgB);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Should no more throw from here on
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (m_cacheResults) {
shun-iwasawa 481b59
        if (imgA->getRaster()->getPixelSize() == 8 ||
shun-iwasawa 481b59
            imgA->getRaster()->getPixelSize() == 16) {
shun-iwasawa 481b59
          // Convert 64-bit / float images to 32 - cached images are supposed to
shun-iwasawa 481b59
          // be 32-bit
Shinya Kitaoka 120a6e
          TRaster32P aux(imgA->getRaster()->getLx(),
Shinya Kitaoka 120a6e
                         imgA->getRaster()->getLy());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          TRop::convert(aux, imgA->getRaster());
Shinya Kitaoka 120a6e
          imgA->setRaster(aux);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        TImageCache::instance()->add(
Shinya Kitaoka 120a6e
            m_renderCacheId + std::to_string(fid.getNumber()), imgA);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      success = true;
Shinya Kitaoka 120a6e
    } catch (...) {
Shinya Kitaoka 120a6e
      // Nothing. The images could not be saved for whatever reason.
Shinya Kitaoka 120a6e
      // Failure is reported.
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return std::make_pair(success, fr);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::doRenderRasterCompleted(const RenderData &renderData) {
Shinya Kitaoka 120a6e
  assert(!(m_cacheResults &&
Shinya Kitaoka 120a6e
           m_levelUpdaterB.get()));  // Cannot cache results on stereoscopy
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  QMutexLocker locker(&m_mutex);
Shinya Kitaoka 120a6e
justburner 69bbaf
  bool allowMT    = Preferences::instance()->getFfmpegMultiThread();
justburner 69bbaf
  bool requireSeq = allowMT ? m_seqRequired : m_movieType;
justburner 69bbaf
Shinya Kitaoka 120a6e
  // Build soundtrack at the first time a frame is completed - and the filetype
Shinya Kitaoka 120a6e
  // is that of a movie.
Shinya Kitaoka 120a6e
  if (m_firstCompletedRaster && m_movieType && !m_st) {
shun-iwasawa a38488
    int boardDuration = addBoard();
shun-iwasawa a38488
Shinya Kitaoka 120a6e
    int from, to;
Shinya Kitaoka 120a6e
    getRange(m_scene, false, from, to);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TLevelP oldLevel(m_levelUpdaterA->getInputLevel());
Shinya Kitaoka 120a6e
    if (oldLevel) {
Shinya Kitaoka 120a6e
      from = std::min(from, oldLevel->begin()->first.getNumber() - 1);
Shinya Kitaoka 120a6e
      to   = std::max(to, (--oldLevel->end())->first.getNumber() - 1);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addSoundtrack(
Shinya Kitaoka 120a6e
        from, to,
shun-iwasawa a38488
        m_scene->getProperties()->getOutputProperties()->getFrameRate(),
shun-iwasawa a38488
        boardDuration);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (m_st) {
Shinya Kitaoka 120a6e
      m_levelUpdaterA->getLevelWriter()->saveSoundTrack(m_st.getPointer());
Shinya Kitaoka 120a6e
      if (m_levelUpdaterB.get())
Shinya Kitaoka 120a6e
        m_levelUpdaterB->getLevelWriter()->saveSoundTrack(m_st.getPointer());
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Output frames must be *cloned*, since the supplied rasters will be
Shinya Kitaoka 120a6e
  // overwritten by m_renderer
Shinya Kitaoka 120a6e
  TRasterP toBeSavedRasA = renderData.m_rasA->clone();
Shinya Kitaoka 120a6e
  TRasterP toBeSavedRasB =
Shinya Kitaoka 120a6e
      renderData.m_rasB ? renderData.m_rasB->clone() : TRasterP();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_toBeSaved[renderData.m_frames[0]] =
Shinya Kitaoka 120a6e
      std::make_pair(toBeSavedRasA, toBeSavedRasB);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_toBeAppliedGamma[renderData.m_frames[0]] = true;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Prepare the cluster's frames to be saved (possibly in the future)
Shinya Kitaoka 120a6e
  std::vector<double>::const_iterator jt;</double>
Shinya Kitaoka 120a6e
  for (jt = renderData.m_frames.begin(), ++jt; jt != renderData.m_frames.end();
Shinya Kitaoka 120a6e
       ++jt) {
Shinya Kitaoka 120a6e
    m_toBeSaved[*jt]        = std::make_pair(toBeSavedRasA, toBeSavedRasB);
Shinya Kitaoka 120a6e
    m_toBeAppliedGamma[*jt] = false;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Attempt flushing as many frames as possible to the level updater(s)
Shinya Kitaoka 120a6e
  while (!m_toBeSaved.empty()) {
Shinya Kitaoka 120a6e
    std::map<double, std::pair<trasterp,="" trasterp="">>::iterator ft =</double,>
Shinya Kitaoka 120a6e
        m_toBeSaved.begin();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // In the *movie type* case, frames must be saved sequentially.
Shinya Kitaoka 120a6e
    // If the frame is not the next one in the sequence, wait until *that* frame
Shinya Kitaoka 120a6e
    // is available.
justburner 69bbaf
    if (requireSeq &&
Shinya Kitaoka 120a6e
        (ft->first != m_framesToBeRendered[m_nextFrameIdxToSave].first))
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // This thread will be the one processing ft - remove it from the map to
Shinya Kitaoka 120a6e
    // prevent another
Shinya Kitaoka 120a6e
    // thread from interfering
shun-iwasawa fc0d80
    double frame                          = ft->first;
Shinya Kitaoka 120a6e
    std::pair<trasterp, trasterp=""> rasters = ft->second;</trasterp,>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ++m_nextFrameIdxToSave;
Shinya Kitaoka 120a6e
    m_toBeSaved.erase(ft);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Save current frame
Shinya Kitaoka 120a6e
    std::pair<bool, int=""> savedFrame;</bool,>
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      // Time the saving procedure
Shinya Kitaoka 120a6e
      struct SaveTimer {
Shinya Kitaoka 120a6e
        int &m_count;
Shinya Kitaoka 120a6e
        SaveTimer(int &count) : m_count(count) {
Shinya Kitaoka 120a6e
          if (m_count++ == 0) TStopWatch::global(0).start();
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
        ~SaveTimer() {
Shinya Kitaoka 120a6e
          if (--m_count == 0) TStopWatch::global(0).stop();
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      } saveTimer(m_savingThreadsCount);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Unlock the mutex only in case this is NOT a movie type. Single images
Shinya Kitaoka 120a6e
      // can be saved concurrently.
Shinya Kitaoka 120a6e
      struct MutexUnlocker {
Shinya Kitaoka 120a6e
        QMutexLocker *m_locker;
Shinya Kitaoka 120a6e
        ~MutexUnlocker() {
Shinya Kitaoka 120a6e
          if (m_locker) m_locker->relock();
Shinya Kitaoka 120a6e
        }
justburner 69bbaf
      } unlocker = {requireSeq ? (QMutexLocker *)0
justburner 69bbaf
                               : (locker.unlock(), &locker)};
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      savedFrame = saveFrame(frame, rasters);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Report status and deal with responses
Shinya Kitaoka 120a6e
    bool okToContinue = true;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    std::set<movierenderer::listener *="">::iterator lt = m_listeners.begin();</movierenderer::listener>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (savedFrame.first) {
Shinya Kitaoka 120a6e
      for (; lt != m_listeners.end(); ++lt)
Shinya Kitaoka 120a6e
        okToContinue &= (*lt)->onFrameCompleted(savedFrame.second);
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      for (; lt != m_listeners.end(); ++lt) {
Shinya Kitaoka 120a6e
        TException e;
Shinya Kitaoka 120a6e
        okToContinue &= (*lt)->onFrameFailed(savedFrame.second, e);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (!okToContinue) {
Shinya Kitaoka 120a6e
      // Some listener invoked termination of the render procedure. It seems
Shinya Kitaoka 120a6e
      // it's their right
Shinya Kitaoka 120a6e
      // to do so. I wonder what happens if two listeners would disagree on the
Shinya Kitaoka 120a6e
      // matter...
Shinya Kitaoka 120a6e
      // BTW stop the rendering, alright.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        int from, to;
Shinya Kitaoka 120a6e
        getRange(m_scene, false, from,
Shinya Kitaoka 120a6e
                 to);  // It's ok since cancels can only happen from Toonz...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        for (int i = from; i < to; i++)
Shinya Kitaoka 120a6e
          TImageCache::instance()->remove(m_renderCacheId +
Shinya Kitaoka 120a6e
                                          std::to_string(i + 1));
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      m_renderer.stopRendering();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      m_levelUpdaterA
Shinya Kitaoka 120a6e
          .reset();  // No more saving. Further attempts to save images
Shinya Kitaoka 120a6e
      m_levelUpdaterB.reset();  // will be rejected and treated as failures.
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_firstCompletedRaster = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::doPreviewRasterCompleted(
Shinya Kitaoka 120a6e
    const RenderData &renderData) {
Shinya Kitaoka 120a6e
  // Most probably unused now. I'm not reviewing this.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(!m_levelUpdaterA.get());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  QMutexLocker sl(&m_mutex);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  QString name = getPreviewName(m_renderSessionId);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TRasterP ras = renderData.m_rasA->clone();
Shinya Kitaoka 120a6e
  if (renderData.m_rasB) {
Shinya Kitaoka 120a6e
    assert(m_renderSettings.m_stereoscopic);
Shinya Kitaoka 120a6e
    TRop::makeStereoRaster(ras, renderData.m_rasB);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TRasterImageP img(ras);
Shinya Kitaoka 120a6e
  img->setDpi(m_xDpi, m_yDpi);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  try {
Shinya Kitaoka 120a6e
    if (renderData.m_info.m_mark != TRasterP())
Shinya Kitaoka 120a6e
      addMark(renderData.m_info.m_mark, img);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (img->getRaster()->getPixelSize() == 8) {
Shinya Kitaoka 120a6e
      TRaster32P aux(img->getRaster()->getLx(), img->getRaster()->getLy());
Shinya Kitaoka 120a6e
      TRop::convert(aux, img->getRaster());
Shinya Kitaoka 120a6e
      img->setRaster(aux);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    QString frameName = name + QString::number(renderData.m_frames[0] + 1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TImageCache::instance()->add(frameName.toStdString(), img);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // controlla se ci sono frame(uguali ad altri) da mettere in cache
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    std::vector<double>::const_iterator jt;</double>
Shinya Kitaoka 120a6e
    for (jt = renderData.m_frames.begin(), ++jt;
Shinya Kitaoka 120a6e
         jt != renderData.m_frames.end(); ++jt) {
Shinya Kitaoka 120a6e
      frameName = name + QString::number(*jt + 1);
Shinya Kitaoka 120a6e
      TImageCache::instance()->add(frameName.toStdString(), img);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } catch (...) {
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::set<movierenderer::listener *="">::iterator listenerIt =</movierenderer::listener>
Shinya Kitaoka 120a6e
      m_listeners.begin();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  bool okToContinue = true;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  for (; listenerIt != m_listeners.end(); ++listenerIt)
Shinya Kitaoka 120a6e
    okToContinue &= (*listenerIt)->onFrameCompleted(renderData.m_frames[0]);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!okToContinue) {
Shinya Kitaoka 120a6e
    // Svuoto la cache nel caso in cui si esce dal render prima della fine
Shinya Kitaoka 120a6e
    int from, to;
Shinya Kitaoka 120a6e
    getRange(m_scene, true, from, to);
Shinya Kitaoka 120a6e
    for (int i = from; i < to; i++) {
Shinya Kitaoka 120a6e
      QString frameName = name + QString::number(i + 1);
Shinya Kitaoka 120a6e
      TImageCache::instance()->remove(frameName.toStdString());
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_renderer.stopRendering();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_firstCompletedRaster = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::onRenderFailure(const RenderData &renderData,
Shinya Kitaoka 120a6e
                                         TException &e) {
Shinya Kitaoka 120a6e
  QMutexLocker sl(&m_mutex);  // Lock as soon as possible.
Shinya Kitaoka 120a6e
                              // No sense making it later in this case!
Shinya Kitaoka 120a6e
  m_failure = true;
Toshihiro Shimizu 890ddd
justburner 69bbaf
  bool allowMT    = Preferences::instance()->getFfmpegMultiThread();
justburner 69bbaf
  bool requireSeq = allowMT ? m_seqRequired : m_movieType;
justburner 69bbaf
Shinya Kitaoka 120a6e
  // If the saver object has already been destroyed - or it was never
Shinya Kitaoka 120a6e
  // created to begin with, nothing to be done
Shinya Kitaoka 120a6e
  if (!m_levelUpdaterA.get()) return;  // The preview case would fall here
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Flush out as much as we can of the frames that were already rendered
Shinya Kitaoka 120a6e
  m_toBeSaved[0.0] =
Shinya Kitaoka 120a6e
      std::make_pair(TRasterP(), TRasterP());  // ?? Why is this ??
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::map<double, std::pair<trasterp,="" trasterp="">>::iterator it =</double,>
Shinya Kitaoka 120a6e
      m_toBeSaved.begin();
Shinya Kitaoka 120a6e
  while (it != m_toBeSaved.end()) {
justburner 69bbaf
    if (requireSeq &&
Shinya Kitaoka 120a6e
        (it->first != m_framesToBeRendered[m_nextFrameIdxToSave].first))
Shinya Kitaoka 120a6e
      break;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // o_o!
Shinya Kitaoka 120a6e
    // I would have expected that at least those frames that were computed could
Shinya Kitaoka 120a6e
    // attempt saving! Why is this not addressed? They're even marked as
Shinya Kitaoka 120a6e
    // 'failed'!
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    double stretchFac = (double)renderData.m_info.m_timeStretchTo /
Shinya Kitaoka 120a6e
                        renderData.m_info.m_timeStretchFrom;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int fr;
Shinya Kitaoka 120a6e
    if (stretchFac != 1)
Shinya Kitaoka 120a6e
      fr = tround(it->first * stretchFac);
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      fr = (int)it->first;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // No saving? Really?
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    std::set<movierenderer::listener *="">::iterator lt = m_listeners.begin();</movierenderer::listener>
Shinya Kitaoka 120a6e
    bool okToContinue                                = true;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    for (; lt != m_listeners.end(); ++lt)
Shinya Kitaoka 120a6e
      okToContinue &= (*lt)->onFrameFailed((int)it->first, e);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (!okToContinue) m_renderer.stopRendering();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    ++m_nextFrameIdxToSave;
Shinya Kitaoka 120a6e
    m_toBeSaved.erase(it++);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::Imp::onRenderFinished(bool isCanceled) {
Shinya Kitaoka 120a6e
  TFilePath levelName(
Shinya Kitaoka 120a6e
      m_levelUpdaterA.get()
Shinya Kitaoka 120a6e
          ? m_fp
Shinya Kitaoka 120a6e
          : TFilePath(getPreviewName(m_renderSessionId).toStdWString()));
Toshihiro Shimizu 890ddd
justburner 69bbaf
  if (m_waitAfterFinish) {
justburner 69bbaf
    // Wait half a second to add some stability before finalizing
justburner 69bbaf
    QEventLoop eloop;
justburner 69bbaf
    QTimer timer;
justburner 69bbaf
    timer.connect(&timer, &QTimer::timeout, &eloop, &QEventLoop::quit);
justburner 69bbaf
    timer.start(500);
justburner 69bbaf
    eloop.exec();
justburner 69bbaf
  }
justburner 69bbaf
Shinya Kitaoka 120a6e
  // Close updaters. After this, the output levels should be finalized on disk.
Shinya Kitaoka 120a6e
  m_levelUpdaterA.reset();
Shinya Kitaoka 120a6e
  m_levelUpdaterB.reset();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!m_failure) {
Shinya Kitaoka 120a6e
    // Inform listeners of the render completion
Shinya Kitaoka 120a6e
    std::set<movierenderer::listener *="">::iterator it;</movierenderer::listener>
Shinya Kitaoka 120a6e
    for (it = m_listeners.begin(); it != m_listeners.end(); ++it)
Shinya Kitaoka 120a6e
      (*it)->onSequenceCompleted(levelName);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // I wonder why listeners are not informed of a failed sequence, btw...
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  release();  // The movieRenderer is released by the render process. It could
Shinya Kitaoka 120a6e
              // eventually be deleted.
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
shun-iwasawa cd4694
//---------------------------------------------------------
shun-iwasawa cd4694
shun-iwasawa a38488
int MovieRenderer::Imp::addBoard() {
shun-iwasawa cd4694
  BoardSettings *boardSettings =
shun-iwasawa cd4694
      m_scene->getProperties()->getOutputProperties()->getBoardSettings();
shun-iwasawa a38488
  if (!boardSettings->isActive()) return 0;
shun-iwasawa cd4694
  int duration = boardSettings->getDuration();
shun-iwasawa a38488
  if (duration == 0) return 0;
shun-iwasawa cd4694
  // Get the image size
shun-iwasawa cd4694
  int shrinkX = m_renderSettings.m_shrinkX,
shun-iwasawa cd4694
      shrinkY = m_renderSettings.m_shrinkY;
shun-iwasawa cd4694
  TDimensionD cameraRes(double(m_frameSize.lx) / shrinkX,
shun-iwasawa cd4694
                        double(m_frameSize.ly) / shrinkY);
shun-iwasawa cd4694
  TDimension cameraResI(cameraRes.lx, cameraRes.ly);
shun-iwasawa cd4694
shun-iwasawa cd4694
  TRaster32P boardRas =
shun-iwasawa cd4694
      boardSettings->getBoardRaster(cameraResI, shrinkX, m_scene);
shun-iwasawa cd4694
shun-iwasawa cd4694
  if (m_levelUpdaterA.get()) {
shun-iwasawa cd4694
    // Flush images
shun-iwasawa cd4694
    try {
shun-iwasawa cd4694
      TRasterImageP img(boardRas);
shun-iwasawa cd4694
      for (int f = 0; f < duration; f++) {
shun-iwasawa cd4694
        m_levelUpdaterA->update(TFrameId(f + 1), img);
shun-iwasawa cd4694
        if (m_levelUpdaterB.get())
shun-iwasawa cd4694
          m_levelUpdaterB->update(TFrameId(f + 1), img);
shun-iwasawa cd4694
      }
shun-iwasawa cd4694
    } catch (...) {
shun-iwasawa cd4694
      // Nothing. The images could not be saved for whatever reason.
shun-iwasawa cd4694
      // Failure is reported.
shun-iwasawa cd4694
    }
shun-iwasawa cd4694
  }
shun-iwasawa a38488
  return duration;
shun-iwasawa cd4694
}
shun-iwasawa cd4694
Toshihiro Shimizu 890ddd
//======================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//======================
Toshihiro Shimizu 890ddd
//    MovieRenderer
Toshihiro Shimizu 890ddd
//----------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
MovieRenderer::MovieRenderer(ToonzScene *scene, const TFilePath &moviePath,
Shinya Kitaoka 120a6e
                             int threadCount, bool cacheResults)
Shinya Kitaoka 120a6e
    : m_imp(new Imp(scene, moviePath, threadCount, cacheResults)) {
Shinya Kitaoka 120a6e
  m_imp->addRef();  // See MovieRenderer::start(). Can't just delete it in the
Shinya Kitaoka 120a6e
                    // dtor.
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
MovieRenderer::~MovieRenderer() { m_imp->release(); }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::setRenderSettings(const TRenderSettings &renderSettings) {
Shinya Kitaoka 120a6e
  m_imp->m_renderSettings = renderSettings;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::setDpi(double xDpi, double yDpi) {
Shinya Kitaoka 120a6e
  m_imp->m_xDpi = xDpi;
Shinya Kitaoka 120a6e
  m_imp->m_yDpi = yDpi;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::addListener(Listener *listener) {
Shinya Kitaoka 120a6e
  m_imp->m_listeners.insert(listener);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::addFrame(double frame, const TFxPair &fxPair) {
Shinya Kitaoka 120a6e
  m_imp->m_framesToBeRendered.push_back(std::make_pair(frame, fxPair));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::enablePrecomputing(bool on) {
Shinya Kitaoka 120a6e
  m_imp->m_renderer.enablePrecomputing(on);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool MovieRenderer::isPrecomputingEnabled() const {
Shinya Kitaoka 120a6e
  return m_imp->m_renderer.isPrecomputingEnabled();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::start() {
Shinya Kitaoka 120a6e
  m_imp->prepareForStart();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Add a reference to MovieRenderer's Imp. The reference is 'owned' by
Shinya Kitaoka 120a6e
  // TRenderer's render process - when it
Shinya Kitaoka 120a6e
  // ends (that is, when notifies onRenderFinished), the reference is released.
Shinya Kitaoka 120a6e
  // As to TRenderer's specifics,
Shinya Kitaoka 120a6e
  // this is ensured to happen only after all the other port notifications for
Shinya Kitaoka 120a6e
  // each frame have been invoked.
Shinya Kitaoka 120a6e
  m_imp->addRef();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Prepare the TRenderer::RenderDatas to render
Shinya Kitaoka 120a6e
  RenderDataVector *datasToBeRendered = new RenderDataVector;
Shinya Kitaoka 120a6e
  size_t i, size = m_imp->m_framesToBeRendered.size();
Shinya Kitaoka 120a6e
  for (i = 0; i < size; ++i)
Shinya Kitaoka 120a6e
    datasToBeRendered->push_back(TRenderer::RenderData(
Shinya Kitaoka 120a6e
        m_imp->m_framesToBeRendered[i].first, m_imp->m_renderSettings,
Shinya Kitaoka 120a6e
        m_imp->m_framesToBeRendered[i].second));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_imp->m_renderer.startRendering(datasToBeRendered);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void MovieRenderer::onCanceled() { m_imp->m_renderer.stopRendering(true); }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TRenderer *MovieRenderer::getTRenderer() {
Shinya Kitaoka 120a6e
  // Again, this is somewhat BAD. The pointed-to object dies together with the
Shinya Kitaoka 120a6e
  // MovieRenderer instance.
Shinya Kitaoka 120a6e
  // Since a TRenderer is already smart-pointer-like, we could just return a
Shinya Kitaoka 120a6e
  // copy - however, it really
luzpaz 27707d
  // shouldn't be that way. Maybe one day we'll revert that and actually use a
Shinya Kitaoka 120a6e
  // smart pointer class.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // For now, no use of this function seems to access the returned pointer
Shinya Kitaoka 120a6e
  // beyond the lifespan of the
Shinya Kitaoka 120a6e
  // associated MovieRenderer instance - so I'm not gonna touch the class
Shinya Kitaoka 120a6e
  // interface.
Shinya Kitaoka 120a6e
  return &m_imp->m_renderer;
Toshihiro Shimizu 890ddd
}