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
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"
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>
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
Toshihiro Shimizu 890ddd
namespace
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
int RenderSessionId = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void addMark(const TRasterP &mark, TRasterImageP img)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TRasterP raster = img->getRaster();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (raster->getLx() >= mark->getLx() && raster->getLy() >= mark->getLy()) {
Toshihiro Shimizu 890ddd
		TRasterP ras = raster->clone();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		int borderx = troundp(0.035 * (ras->getLx() - mark->getLx()));
Toshihiro Shimizu 890ddd
		int bordery = troundp(0.035 * (ras->getLy() - mark->getLy()));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TRect rect = TRect(borderx, bordery, borderx + mark->getLx() - 1, bordery + mark->getLy() - 1);
Toshihiro Shimizu 890ddd
		TRop::over(ras->extract(rect), mark);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		img->setRaster(ras);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void getRange(ToonzScene *scene, bool isPreview, int &from, int &to)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TSceneProperties *sprop = scene->getProperties();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int step;
Toshihiro Shimizu 890ddd
	if (isPreview)
Toshihiro Shimizu 890ddd
		sprop->getPreviewProperties()->getRange(from, to, step);
Toshihiro Shimizu 890ddd
	else
Toshihiro Shimizu 890ddd
		sprop->getOutputProperties()->getRange(from, to, step);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (to < 0) {
Toshihiro Shimizu 890ddd
		TXsheet *xs = scene->getXsheet();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// NOTE: Use of numeric_limits::min is justified since the type is *INTERGRAL*.
Toshihiro Shimizu 890ddd
		from = (std::numeric_limits<int>::max)(), to = (std::numeric_limits<int>::min)();</int></int>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		for (int k = 0; k < xs->getColumnCount(); ++k) {
Toshihiro Shimizu 890ddd
			int r0, r1;
Toshihiro Shimizu 890ddd
			xs->getCellRange(k, r0, r1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			from = tmin(from, r0), to = tmax(to, r1);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
QString getPreviewName(unsigned long renderSessionId)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	return "previewed" + QString::number(renderSessionId) + ".noext";
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//**************************************************************************
Toshihiro Shimizu 890ddd
//    MovieRenderer::Imp  definition
Toshihiro Shimizu 890ddd
//**************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
class MovieRenderer::Imp : public TRenderPort, public TSmartObject
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	ToonzScene *m_scene;
Toshihiro Shimizu 890ddd
	TRenderer m_renderer;
Toshihiro Shimizu 890ddd
	TFilePath m_fp;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRenderSettings m_renderSettings;
Toshihiro Shimizu 890ddd
	TDimension m_frameSize;
Toshihiro Shimizu 890ddd
	double m_xDpi, m_yDpi;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	std::set<movierenderer::listener *=""> m_listeners;</movierenderer::listener>
Toshihiro Shimizu 890ddd
	std::auto_ptr<levelupdater> m_levelUpdaterA, m_levelUpdaterB;</levelupdater>
Toshihiro Shimizu 890ddd
	TSoundTrackP m_st;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	std::map<double, std::pair<trasterp,="" trasterp="">> m_toBeSaved;</double,>
Toshihiro Shimizu 890ddd
	std::vector<pair<double, tfxpair="">> m_framesToBeRendered;</pair<double,>
Toshihiro Shimizu 890ddd
	std::string m_renderCacheId;
Toshihiro Shimizu 890ddd
	/*--- 同じラスタのキャッシュを使いまわすとき、
Toshihiro Shimizu 890ddd
		最初のものだけガンマをかけ、以降はそれを使いまわすようにする。
Toshihiro Shimizu 890ddd
	---*/
Toshihiro Shimizu 890ddd
	std::map<double, bool=""> m_toBeAppliedGamma;</double,>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TThread::Mutex m_mutex;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int m_renderSessionId;
Toshihiro Shimizu 890ddd
	long m_whiteSample;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int m_nextFrameIdxToSave;
Toshihiro Shimizu 890ddd
	int m_savingThreadsCount;
Toshihiro Shimizu 890ddd
	bool m_firstCompletedRaster;
Toshihiro Shimizu 890ddd
	bool m_failure;
Toshihiro Shimizu 890ddd
	bool m_cacheResults;
Toshihiro Shimizu 890ddd
	bool m_preview;
Toshihiro Shimizu 890ddd
	bool m_movieType;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	Imp(ToonzScene *scene, const TFilePath &moviePath, int threadCount, bool cacheResults);
Toshihiro Shimizu 890ddd
	~Imp();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// TRenderPort methods
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void onRenderRasterCompleted(const RenderData &renderData);
Toshihiro Shimizu 890ddd
	void onRenderFailure(const RenderData &renderData, TException &e);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	/*-- キャンセル時にはm_overallRenderedRegionを更新しない --*/
Toshihiro Shimizu 890ddd
	virtual void onRenderFinished(bool isCanceled = false);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void doRenderRasterCompleted(const RenderData &renderData);
Toshihiro Shimizu 890ddd
	void doPreviewRasterCompleted(const RenderData &renderData);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Helper methods
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void prepareForStart();
Toshihiro Shimizu 890ddd
	void addSoundtrack(int r0, int r1, double fps);
Toshihiro Shimizu 890ddd
	void postProcessImage(const TRasterImageP &img, bool has64bitOutputSupport, const TRasterP &mark, int frame);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//! Saves the specified rasters at the specified time; returns whether the frames were successfully saved, and
Toshihiro Shimizu 890ddd
	//! the associated time-adjusted level frame.
Toshihiro Shimizu 890ddd
	std::pair<bool, int=""> saveFrame(double frame, const std::pair<trasterp, trasterp=""> &rasters);</trasterp,></bool,>
Toshihiro Shimizu 890ddd
	std::string getRenderCacheId();
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
MovieRenderer::Imp::Imp(ToonzScene *scene,
Toshihiro Shimizu 890ddd
						const TFilePath &moviePath,
Toshihiro Shimizu 890ddd
						int threadCount,
Toshihiro Shimizu 890ddd
						bool cacheResults)
Toshihiro Shimizu 890ddd
	: m_scene(scene), m_renderer(threadCount), m_fp(moviePath), m_frameSize(scene->getCurrentCamera()->getRes()), m_xDpi(72), m_yDpi(72), m_renderSessionId(RenderSessionId++), m_nextFrameIdxToSave(0), m_savingThreadsCount(0), m_whiteSample(0), m_firstCompletedRaster(true) //< I know, sounds weird - it's just set to false
Toshihiro Shimizu 890ddd
	  ,
Toshihiro Shimizu 890ddd
	  m_failure(false) //  AFTER the first completed raster gets processed
Toshihiro Shimizu 890ddd
	  ,
Toshihiro Shimizu 890ddd
	  m_cacheResults(cacheResults), m_preview(moviePath.isEmpty()), m_movieType(isMovieType(moviePath))
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_renderCacheId = m_fp.withName(m_fp.getName() + "#RENDERID" + QString::number(m_renderSessionId).toStdString())
Toshihiro Shimizu 890ddd
						  .getLevelName();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_renderer.addPort(this);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
MovieRenderer::Imp::~Imp()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_renderer.removePort(this); // Please, note: a TRenderer instance is currently a shared-pointer-like
Toshihiro Shimizu 890ddd
} // object to a private worker. *That* object may outlive the TRenderer instance.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::prepareForStart()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	struct locals {
Toshihiro Shimizu 890ddd
		static void eraseUncompatibleExistingLevel(const TFilePath &fp, const TDimension &imageSize) // nothrow
Toshihiro Shimizu 890ddd
		{
Toshihiro Shimizu 890ddd
			assert(!fp.isEmpty());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			if (TSystem::doesExistFileOrLevel(fp)) {
Toshihiro Shimizu 890ddd
				bool remove = false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				// In case the raster specifics are different from those of a currently
Toshihiro Shimizu 890ddd
				// existing movie, erase it
Toshihiro Shimizu 890ddd
				try {
Toshihiro Shimizu 890ddd
					TLevelReaderP lr(fp);
Toshihiro Shimizu 890ddd
					lr->loadInfo();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
					const TImageInfo *info = lr->getImageInfo();
Toshihiro Shimizu 890ddd
					if (!info || info->m_lx != imageSize.lx || info->m_ly != imageSize.ly)
Toshihiro Shimizu 890ddd
						TSystem::removeFileOrLevel(fp); // nothrow
Toshihiro Shimizu 890ddd
				} catch (...) {
Toshihiro Shimizu 890ddd
					// Same if the level could not be read/opened
Toshihiro Shimizu 890ddd
					TSystem::removeFileOrLevel(fp); // nothrow
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				// NOTE: The level removal procedure could still fail.
Toshihiro Shimizu 890ddd
				// In this case, no signaling takes place. The level readers will throw
Toshihiro Shimizu 890ddd
				// when the time to write on the file comes, leading to a render failure.
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TOutputProperties *oprop = m_scene->getProperties()->getOutputProperties();
Toshihiro Shimizu 890ddd
	double frameRate = (double)oprop->getFrameRate();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	/*-- Frame rate の stretch --*/
Toshihiro Shimizu 890ddd
	double stretchFactor = oprop->getRenderSettings().m_timeStretchTo / oprop->getRenderSettings().m_timeStretchFrom;
Toshihiro Shimizu 890ddd
	frameRate *= stretchFactor;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Get the shrink
Toshihiro Shimizu 890ddd
	int shrinkX = m_renderSettings.m_shrinkX, shrinkY = m_renderSettings.m_shrinkY;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Build the render area
Toshihiro Shimizu 890ddd
	TPointD cameraPos(-0.5 * m_frameSize.lx, -0.5 * m_frameSize.ly);
Toshihiro Shimizu 890ddd
	TDimensionD cameraRes(double(m_frameSize.lx) / shrinkX, double(m_frameSize.ly) / shrinkY);
Toshihiro Shimizu 890ddd
	TDimension cameraResI(cameraRes.lx, cameraRes.ly);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRectD renderArea(cameraPos.x, cameraPos.y, cameraPos.x + cameraRes.lx, cameraPos.y + cameraRes.ly);
Toshihiro Shimizu 890ddd
	setRenderArea(renderArea);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!m_fp.isEmpty()) {
Toshihiro Shimizu 890ddd
		try // Construction of a LevelUpdater may throw (well, almost ANY operation on a LevelUpdater
Toshihiro Shimizu 890ddd
		{   // could throw). But due to backward compatibility this function is assumed to be non-throwing.
Toshihiro Shimizu 890ddd
			if (!m_renderSettings.m_stereoscopic) {
Toshihiro Shimizu 890ddd
				locals::eraseUncompatibleExistingLevel(m_fp, cameraResI);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				m_levelUpdaterA.reset(new LevelUpdater(m_fp, oprop->getFileFormatProperties(m_fp.getType())));
Toshihiro Shimizu 890ddd
				m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate);
Toshihiro Shimizu 890ddd
			} else {
Toshihiro Shimizu 890ddd
				TFilePath leftFp = m_fp.withName(m_fp.getName() + "_l");
Toshihiro Shimizu 890ddd
				TFilePath rightFp = m_fp.withName(m_fp.getName() + "_r");
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				locals::eraseUncompatibleExistingLevel(leftFp, cameraResI);
Toshihiro Shimizu 890ddd
				locals::eraseUncompatibleExistingLevel(rightFp, cameraResI);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				m_levelUpdaterA.reset(new LevelUpdater(leftFp, oprop->getFileFormatProperties(leftFp.getType())));
Toshihiro Shimizu 890ddd
				m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				m_levelUpdaterB.reset(new LevelUpdater(rightFp, oprop->getFileFormatProperties(rightFp.getType())));
Toshihiro Shimizu 890ddd
				m_levelUpdaterB->getLevelWriter()->setFrameRate(frameRate);
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
		} catch (...) {
Toshihiro Shimizu 890ddd
			// If we get here, it's because one of the LevelUpdaters could not be created. So, let's say
Toshihiro Shimizu 890ddd
			// that if one could not be created, then ALL OF THEM couldn't (ie saving is not possible at all).
Toshihiro Shimizu 890ddd
			m_levelUpdaterA.reset();
Toshihiro Shimizu 890ddd
			m_levelUpdaterB.reset();
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::addSoundtrack(int r0, int r1, double fps)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TCG_ASSERT(r0 <= r1, return );
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TXsheet::SoundProperties *prop = new TXsheet::SoundProperties(); // Ownership will be surrendered ...
Toshihiro Shimizu 890ddd
	prop->m_frameRate = fps;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TSoundTrack *snd = m_scene->getXsheet()->makeSound(prop); // ... here
Toshihiro Shimizu 890ddd
	if (!snd) {
Toshihiro Shimizu 890ddd
		// No soundtrack
Toshihiro Shimizu 890ddd
		m_whiteSample = (r1 - r0 + 1) * 918; // 918?? wtf... I don't think it has
Toshihiro Shimizu 890ddd
		return;								 // any arcane meaning - but i'm not touching it.
Toshihiro Shimizu 890ddd
	}										 // My impression would be that... no sound implies
Toshihiro Shimizu 890ddd
											 // no access to m_whiteSample, no?
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double samplePerFrame = snd->getSampleRate() / fps;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Extract the useful part of scene soundtrack
Toshihiro Shimizu 890ddd
	TSoundTrackP snd1 = snd->extract((TINT32)(r0 * samplePerFrame),
Toshihiro Shimizu 890ddd
									 (TINT32)(r1 * samplePerFrame));
Toshihiro Shimizu 890ddd
	assert(!m_st);
Toshihiro Shimizu 890ddd
	if (!m_st) {
Toshihiro Shimizu 890ddd
		// First, add white sound before the 'from' instant
Toshihiro Shimizu 890ddd
		m_st = TSoundTrack::create(snd1->getFormat(), m_whiteSample);
Toshihiro Shimizu 890ddd
		m_whiteSample = 0; // Why?  Probably being pedantic here... I guess
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Then, add the rest
Toshihiro Shimizu 890ddd
	TINT32 fromSample = m_st->getSampleCount();
Toshihiro Shimizu 890ddd
	TINT32 numSample = tmax(TINT32((r1 - r0 + 1) * samplePerFrame),
Toshihiro Shimizu 890ddd
							snd1->getSampleCount());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_st = TSop::insertBlank(m_st, fromSample, numSample + m_whiteSample);
Toshihiro Shimizu 890ddd
	m_st->copy(snd1, TINT32(fromSample + m_whiteSample));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_whiteSample = 0;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::onRenderRasterCompleted(const RenderData &renderData)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (m_preview)
Toshihiro Shimizu 890ddd
		doPreviewRasterCompleted(renderData);
Toshihiro Shimizu 890ddd
	else
Toshihiro Shimizu 890ddd
		doRenderRasterCompleted(renderData);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::postProcessImage(const TRasterImageP &img, bool has64bitOutputSupport, const TRasterP &mark, int frame)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	img->setDpi(m_xDpi, m_yDpi);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (img->getRaster()->getPixelSize() == 8 && !has64bitOutputSupport) {
Toshihiro Shimizu 890ddd
		TRaster32P aux(img->getRaster()->getLx(), img->getRaster()->getLy());
Toshihiro Shimizu 890ddd
		TRop::convert(aux, img->getRaster());
Toshihiro Shimizu 890ddd
		img->setRaster(aux);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (mark)
Toshihiro Shimizu 890ddd
		addMark(mark, img);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (Preferences::instance()->isSceneNumberingEnabled())
Toshihiro Shimizu 890ddd
		TRasterImageUtils::addGlobalNumbering(img, m_scene->getSceneName(), frame);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
std::pair<bool, int=""> MovieRenderer::Imp::saveFrame(double frame, const std::pair<trasterp, trasterp=""> &rasters)</trasterp,></bool,>
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	bool success = false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build the frame number to write to
Toshihiro Shimizu 890ddd
	double stretchFac = double(m_renderSettings.m_timeStretchTo) / m_renderSettings.m_timeStretchFrom;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int fr = (stretchFac != 1) ? tround(frame * stretchFac) : int(frame);
Toshihiro Shimizu 890ddd
	TFrameId fid(fr + 1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (m_levelUpdaterA.get()) {
Toshihiro Shimizu 890ddd
		assert(m_levelUpdaterB.get() || !rasters.second);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Analyze writer
Toshihiro Shimizu 890ddd
		bool has64bitOutputSupport = false;
Toshihiro Shimizu 890ddd
		{
Toshihiro Shimizu 890ddd
			if (TImageWriterP writerA = m_levelUpdaterA->getLevelWriter()->getFrameWriter(fid))
Toshihiro Shimizu 890ddd
				has64bitOutputSupport = writerA->is64bitOutputSupported();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			// NOTE: If the writer could not be retrieved, the updater will throw.
Toshihiro Shimizu 890ddd
			// Failure will be catched then.
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Prepare the images to be flushed
Toshihiro Shimizu 890ddd
		TRasterP rasterA = rasters.first, rasterB = rasters.second;
Toshihiro Shimizu 890ddd
		assert(rasterA);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		/*--- 同じラスタのキャッシュを使いまわすとき、
Toshihiro Shimizu 890ddd
		最初のものだけガンマをかけ、以降はそれを使いまわすようにする。
Toshihiro Shimizu 890ddd
	---*/
Toshihiro Shimizu 890ddd
		if (m_renderSettings.m_gamma != 1.0 && m_toBeAppliedGamma[frame]) {
Toshihiro Shimizu 890ddd
			TRop::gammaCorrect(rasterA, m_renderSettings.m_gamma);
Toshihiro Shimizu 890ddd
			if (rasterB)
Toshihiro Shimizu 890ddd
				TRop::gammaCorrect(rasterB, m_renderSettings.m_gamma);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Flush images
Toshihiro Shimizu 890ddd
		try {
Toshihiro Shimizu 890ddd
			TRasterImageP imgA(rasterA);
Toshihiro Shimizu 890ddd
			postProcessImage(imgA, has64bitOutputSupport, m_renderSettings.m_mark, fid.getNumber());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_levelUpdaterA->update(fid, imgA);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			if (rasterB) {
Toshihiro Shimizu 890ddd
				TRasterImageP imgB(rasterB);
Toshihiro Shimizu 890ddd
				postProcessImage(imgB, has64bitOutputSupport, m_renderSettings.m_mark, fid.getNumber());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				m_levelUpdaterB->update(fid, imgB);
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			// Should no more throw from here on
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			if (m_cacheResults) {
Toshihiro Shimizu 890ddd
				if (imgA->getRaster()->getPixelSize() == 8) {
Toshihiro Shimizu 890ddd
					// Convert 64-bit images to 32 - cached images are supposed to be 32-bit
Toshihiro Shimizu 890ddd
					TRaster32P aux(imgA->getRaster()->getLx(), imgA->getRaster()->getLy());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
					TRop::convert(aux, imgA->getRaster());
Toshihiro Shimizu 890ddd
					imgA->setRaster(aux);
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				TImageCache::instance()->add(m_renderCacheId + toString(fid.getNumber()), imgA);
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			success = true;
Toshihiro Shimizu 890ddd
		} catch (...) {
Toshihiro Shimizu 890ddd
			// Nothing. The images could not be saved for whatever reason.
Toshihiro Shimizu 890ddd
			// Failure is reported.
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return std::make_pair(success, fr);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::doRenderRasterCompleted(const RenderData &renderData)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	assert(!(m_cacheResults && m_levelUpdaterB.get())); // Cannot cache results on stereoscopy
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QMutexLocker locker(&m_mutex);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build soundtrack at the first time a frame is completed - and the filetype is that of a movie.
Toshihiro Shimizu 890ddd
	if (m_firstCompletedRaster && m_movieType && !m_st) {
Toshihiro Shimizu 890ddd
		int from, to;
Toshihiro Shimizu 890ddd
		getRange(m_scene, false, from, to);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TLevelP oldLevel(m_levelUpdaterA->getInputLevel());
Toshihiro Shimizu 890ddd
		if (oldLevel) {
Toshihiro Shimizu 890ddd
			from = tmin(from, oldLevel->begin()->first.getNumber() - 1);
Toshihiro Shimizu 890ddd
			to = tmax(to, (--oldLevel->end())->first.getNumber() - 1);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		addSoundtrack(from, to, m_scene->getProperties()->getOutputProperties()->getFrameRate());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (m_st) {
Toshihiro Shimizu 890ddd
			m_levelUpdaterA->getLevelWriter()->saveSoundTrack(m_st.getPointer());
Toshihiro Shimizu 890ddd
			if (m_levelUpdaterB.get())
Toshihiro Shimizu 890ddd
				m_levelUpdaterB->getLevelWriter()->saveSoundTrack(m_st.getPointer());
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Output frames must be *cloned*, since the supplied rasters will be overwritten by m_renderer
Toshihiro Shimizu 890ddd
	TRasterP toBeSavedRasA = renderData.m_rasA->clone();
Toshihiro Shimizu 890ddd
	TRasterP toBeSavedRasB = renderData.m_rasB ? renderData.m_rasB->clone() : TRasterP();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_toBeSaved[renderData.m_frames[0]] = std::make_pair(toBeSavedRasA, toBeSavedRasB);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_toBeAppliedGamma[renderData.m_frames[0]] = true;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Prepare the cluster's frames to be saved (possibly in the future)
Toshihiro Shimizu 890ddd
	std::vector<double>::const_iterator jt;</double>
Toshihiro Shimizu 890ddd
	for (jt = renderData.m_frames.begin(), ++jt; jt != renderData.m_frames.end(); ++jt) {
Toshihiro Shimizu 890ddd
		m_toBeSaved[*jt] = std::make_pair(toBeSavedRasA, toBeSavedRasB);
Toshihiro Shimizu 890ddd
		m_toBeAppliedGamma[*jt] = false;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Attempt flushing as many frames as possible to the level updater(s)
Toshihiro Shimizu 890ddd
	while (!m_toBeSaved.empty()) {
Toshihiro Shimizu 890ddd
		std::map<double, std::pair<trasterp,="" trasterp="">>::iterator ft = m_toBeSaved.begin();</double,>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// In the *movie type* case, frames must be saved sequentially.
Toshihiro Shimizu 890ddd
		// If the frame is not the next one in the sequence, wait until *that* frame is available.
Toshihiro Shimizu 890ddd
		if (m_movieType && (ft->first != m_framesToBeRendered[m_nextFrameIdxToSave].first))
Toshihiro Shimizu 890ddd
			break;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// This thread will be the one processing ft - remove it from the map to prevent another
Toshihiro Shimizu 890ddd
		// thread from interfering
Toshihiro Shimizu 890ddd
		double frame = ft->first;
Toshihiro Shimizu 890ddd
		std::pair<trasterp, trasterp=""> rasters = ft->second;</trasterp,>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		++m_nextFrameIdxToSave;
Toshihiro Shimizu 890ddd
		m_toBeSaved.erase(ft);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Save current frame
Toshihiro Shimizu 890ddd
		std::pair<bool, int=""> savedFrame;</bool,>
Toshihiro Shimizu 890ddd
		{
Toshihiro Shimizu 890ddd
			// Time the saving procedure
Toshihiro Shimizu 890ddd
			struct SaveTimer {
Toshihiro Shimizu 890ddd
				int &m_count;
Toshihiro Shimizu 890ddd
				SaveTimer(int &count) : m_count(count)
Toshihiro Shimizu 890ddd
				{
Toshihiro Shimizu 890ddd
					if (m_count++ == 0)
Toshihiro Shimizu 890ddd
						TStopWatch::global(0).start();
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
				~SaveTimer()
Toshihiro Shimizu 890ddd
				{
Toshihiro Shimizu 890ddd
					if (--m_count == 0)
Toshihiro Shimizu 890ddd
						TStopWatch::global(0).stop();
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
			} saveTimer(m_savingThreadsCount);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			// Unlock the mutex only in case this is NOT a movie type. Single images can be saved concurrently.
Toshihiro Shimizu 890ddd
			struct MutexUnlocker {
Toshihiro Shimizu 890ddd
				QMutexLocker *m_locker;
Toshihiro Shimizu 890ddd
				~MutexUnlocker()
Toshihiro Shimizu 890ddd
				{
Toshihiro Shimizu 890ddd
					if (m_locker)
Toshihiro Shimizu 890ddd
						m_locker->relock();
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
			} unlocker = {m_movieType ? (QMutexLocker *)0 : (locker.unlock(), &locker)};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			savedFrame = saveFrame(frame, rasters);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Report status and deal with responses
Toshihiro Shimizu 890ddd
		bool okToContinue = true;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		std::set<movierenderer::listener *="">::iterator lt = m_listeners.begin();</movierenderer::listener>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (savedFrame.first) {
Toshihiro Shimizu 890ddd
			for (; lt != m_listeners.end(); ++lt)
Toshihiro Shimizu 890ddd
				okToContinue &= (*lt)->onFrameCompleted(savedFrame.second);
Toshihiro Shimizu 890ddd
		} else {
Toshihiro Shimizu 890ddd
			for (; lt != m_listeners.end(); ++lt) {
Toshihiro Shimizu 890ddd
				TException e;
Toshihiro Shimizu 890ddd
				okToContinue &= (*lt)->onFrameFailed(savedFrame.second, e);
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (!okToContinue) {
Toshihiro Shimizu 890ddd
			// Some listener invoked termination of the render procedure. It seems it's their right
Toshihiro Shimizu 890ddd
			// to do so. I wonder what happens if two listeners would disagree on the matter...
Toshihiro Shimizu 890ddd
			// BTW stop the rendering, alright.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			{
Toshihiro Shimizu 890ddd
				int from, to;
Toshihiro Shimizu 890ddd
				getRange(m_scene, false, from, to); // It's ok since cancels can only happen from Toonz...
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				for (int i = from; i < to; i++)
Toshihiro Shimizu 890ddd
					TImageCache::instance()->remove(m_renderCacheId + toString(i + 1));
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_renderer.stopRendering();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_levelUpdaterA.reset(); // No more saving. Further attempts to save images
Toshihiro Shimizu 890ddd
			m_levelUpdaterB.reset(); // will be rejected and treated as failures.
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_firstCompletedRaster = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::doPreviewRasterCompleted(const RenderData &renderData)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	// Most probably unused now. I'm not reviewing this.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	assert(!m_levelUpdaterA.get());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QMutexLocker sl(&m_mutex);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QString name = getPreviewName(m_renderSessionId);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRasterP ras = renderData.m_rasA->clone();
Toshihiro Shimizu 890ddd
	if (renderData.m_rasB) {
Toshihiro Shimizu 890ddd
		assert(m_renderSettings.m_stereoscopic);
Toshihiro Shimizu 890ddd
		TRop::makeStereoRaster(ras, renderData.m_rasB);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRasterImageP img(ras);
Toshihiro Shimizu 890ddd
	img->setDpi(m_xDpi, m_yDpi);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	try {
Toshihiro Shimizu 890ddd
		if (renderData.m_info.m_mark != TRasterP())
Toshihiro Shimizu 890ddd
			addMark(renderData.m_info.m_mark, img);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (img->getRaster()->getPixelSize() == 8) {
Toshihiro Shimizu 890ddd
			TRaster32P aux(img->getRaster()->getLx(), img->getRaster()->getLy());
Toshihiro Shimizu 890ddd
			TRop::convert(aux, img->getRaster());
Toshihiro Shimizu 890ddd
			img->setRaster(aux);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		QString frameName = name + QString::number(renderData.m_frames[0] + 1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TImageCache::instance()->add(frameName.toStdString(), img);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		//controlla se ci sono frame(uguali ad altri) da mettere in cache
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		std::vector<double>::const_iterator jt;</double>
Toshihiro Shimizu 890ddd
		for (jt = renderData.m_frames.begin(), ++jt; jt != renderData.m_frames.end(); ++jt) {
Toshihiro Shimizu 890ddd
			frameName = name + QString::number(*jt + 1);
Toshihiro Shimizu 890ddd
			TImageCache::instance()->add(frameName.toStdString(), img);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	} catch (...) {
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	std::set<movierenderer::listener *="">::iterator listenerIt = m_listeners.begin();</movierenderer::listener>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	bool okToContinue = true;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	for (; listenerIt != m_listeners.end(); ++listenerIt)
Toshihiro Shimizu 890ddd
		okToContinue &= (*listenerIt)->onFrameCompleted(renderData.m_frames[0]);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!okToContinue) {
Toshihiro Shimizu 890ddd
		// Svuoto la cache nel caso in cui si esce dal render prima della fine
Toshihiro Shimizu 890ddd
		int from, to;
Toshihiro Shimizu 890ddd
		getRange(m_scene, true, from, to);
Toshihiro Shimizu 890ddd
		for (int i = from; i < to; i++) {
Toshihiro Shimizu 890ddd
			QString frameName = name + QString::number(i + 1);
Toshihiro Shimizu 890ddd
			TImageCache::instance()->remove(frameName.toStdString());
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_renderer.stopRendering();
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_firstCompletedRaster = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::onRenderFailure(const RenderData &renderData, TException &e)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	QMutexLocker sl(&m_mutex); // Lock as soon as possible.
Toshihiro Shimizu 890ddd
							   // No sense making it later in this case!
Toshihiro Shimizu 890ddd
	m_failure = true;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// If the saver object has already been destroyed - or it was never
Toshihiro Shimizu 890ddd
	// created to begin with, nothing to be done
Toshihiro Shimizu 890ddd
	if (!m_levelUpdaterA.get())
Toshihiro Shimizu 890ddd
		return; // The preview case would fall here
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Flush out as much as we can of the frames that were already rendered
Toshihiro Shimizu 890ddd
	m_toBeSaved[0.0] = std::make_pair(TRasterP(), TRasterP()); // ?? Why is this ??
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	std::map<double, std::pair<trasterp,="" trasterp="">>::iterator it = m_toBeSaved.begin();</double,>
Toshihiro Shimizu 890ddd
	while (it != m_toBeSaved.end()) {
Toshihiro Shimizu 890ddd
		if (m_movieType && (it->first != m_framesToBeRendered[m_nextFrameIdxToSave].first))
Toshihiro Shimizu 890ddd
			break;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// o_o!
Toshihiro Shimizu 890ddd
		// I would have expected that at least those frames that were computed could
Toshihiro Shimizu 890ddd
		// attempt saving! Why is this not addressed? They're even marked as 'failed'!
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		double stretchFac = (double)renderData.m_info.m_timeStretchTo / renderData.m_info.m_timeStretchFrom;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		int fr;
Toshihiro Shimizu 890ddd
		if (stretchFac != 1)
Toshihiro Shimizu 890ddd
			fr = tround(it->first * stretchFac);
Toshihiro Shimizu 890ddd
		else
Toshihiro Shimizu 890ddd
			fr = (int)it->first;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// No saving? Really?
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		std::set<movierenderer::listener *="">::iterator lt = m_listeners.begin();</movierenderer::listener>
Toshihiro Shimizu 890ddd
		bool okToContinue = true;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		for (; lt != m_listeners.end(); ++lt)
Toshihiro Shimizu 890ddd
			okToContinue &= (*lt)->onFrameFailed((int)it->first, e);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (!okToContinue)
Toshihiro Shimizu 890ddd
			m_renderer.stopRendering();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		++m_nextFrameIdxToSave;
Toshihiro Shimizu 890ddd
		m_toBeSaved.erase(it++);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::Imp::onRenderFinished(bool isCanceled)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TFilePath levelName(m_levelUpdaterA.get() ? m_fp : TFilePath(getPreviewName(m_renderSessionId).toStdWString()));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Close updaters. After this, the output levels should be finalized on disk.
Toshihiro Shimizu 890ddd
	m_levelUpdaterA.reset();
Toshihiro Shimizu 890ddd
	m_levelUpdaterB.reset();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!m_failure) {
Toshihiro Shimizu 890ddd
		// Inform listeners of the render completion
Toshihiro Shimizu 890ddd
		std::set<movierenderer::listener *="">::iterator it;</movierenderer::listener>
Toshihiro Shimizu 890ddd
		for (it = m_listeners.begin(); it != m_listeners.end(); ++it)
Toshihiro Shimizu 890ddd
			(*it)->onSequenceCompleted(levelName);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// I wonder why listeners are not informed of a failed sequence, btw...
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	release(); // The movieRenderer is released by the render process. It could eventually be deleted.
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//======================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//======================
Toshihiro Shimizu 890ddd
//    MovieRenderer
Toshihiro Shimizu 890ddd
//----------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
MovieRenderer::MovieRenderer(ToonzScene *scene, const TFilePath &moviePath, int threadCount, bool cacheResults)
Toshihiro Shimizu 890ddd
	: m_imp(new Imp(scene, moviePath, threadCount, cacheResults))
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->addRef(); // See MovieRenderer::start(). Can't just delete it in the dtor.
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
MovieRenderer::~MovieRenderer()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->release();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::setRenderSettings(const TRenderSettings &renderSettings)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->m_renderSettings = renderSettings;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::setDpi(double xDpi, double yDpi)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->m_xDpi = xDpi;
Toshihiro Shimizu 890ddd
	m_imp->m_yDpi = yDpi;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::addListener(Listener *listener)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->m_listeners.insert(listener);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::addFrame(double frame, const TFxPair &fxPair)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->m_framesToBeRendered.push_back(std::make_pair(frame, fxPair));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::enablePrecomputing(bool on)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->m_renderer.enablePrecomputing(on);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
bool MovieRenderer::isPrecomputingEnabled() const
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	return m_imp->m_renderer.isPrecomputingEnabled();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::start()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->prepareForStart();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Add a reference to MovieRenderer's Imp. The reference is 'owned' by TRenderer's render process - when it
Toshihiro Shimizu 890ddd
	//ends (that is, when notifies onRenderFinished), the reference is released. As to TRenderer's specifics,
Toshihiro Shimizu 890ddd
	//this is ensured to happen only after all the other port notifications for each frame have been invoked.
Toshihiro Shimizu 890ddd
	m_imp->addRef();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Prepare the TRenderer::RenderDatas to render
Toshihiro Shimizu 890ddd
	RenderDataVector *datasToBeRendered = new RenderDataVector;
Toshihiro Shimizu 890ddd
	size_t i, size = m_imp->m_framesToBeRendered.size();
Toshihiro Shimizu 890ddd
	for (i = 0; i < size; ++i)
Toshihiro Shimizu 890ddd
		datasToBeRendered->push_back(TRenderer::RenderData(
Toshihiro Shimizu 890ddd
			m_imp->m_framesToBeRendered[i].first,
Toshihiro Shimizu 890ddd
			m_imp->m_renderSettings,
Toshihiro Shimizu 890ddd
			m_imp->m_framesToBeRendered[i].second));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_imp->m_renderer.startRendering(datasToBeRendered);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void MovieRenderer::onCanceled()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_imp->m_renderer.stopRendering(true);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
TRenderer *MovieRenderer::getTRenderer()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	// Again, this is somewhat BAD. The pointed-to object dies together with the MovieRenderer instance.
Toshihiro Shimizu 890ddd
	// Since a TRenderer is already smart-pointer-like, we could just return a copy - however, it really
Toshihiro Shimizu 890ddd
	// shouln't be that way. Maybe one day we'll revert that and actually use a smart pointer class.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// For now, no use of this function seems to access the returned pointer beyond the lifespan of the
Toshihiro Shimizu 890ddd
	// associated MovieRenderer instance - so I'm not gonna touch the class interface.
Toshihiro Shimizu 890ddd
	return &m_imp->m_renderer;
Toshihiro Shimizu 890ddd
}