Blob Blame Raw


// TnzLib includes
#include "toonz/toonzscene.h"
#include "toonz/preferences.h"
#include "toonz/sceneproperties.h"
#include "toutputproperties.h"

// TnzBase includes
#include "tcli.h"
#include "tenv.h"
#include "tpluginmanager.h"
#include "trasterfx.h"

// TnzCore includes
#include "tsystem.h"
#include "tstream.h"
#include "tfilepath_io.h"
#include "tconvert.h"
#include "tiio_std.h"
#include "timage_io.h"
#include "tnzimage.h"
#include "tlevel.h"
#include "tlevel_io.h"
#include "tpalette.h"
#include "tropcm.h"
#include "trasterimage.h"
#include "tvectorimage.h"
#include "tvectorrenderdata.h"
#include "tofflinegl.h"

using namespace std;
using namespace TCli;

typedef ArgumentT<TFilePath> FilePathArgument;
typedef QualifierT<TFilePath> FilePathQualifier;

#define RENDER_LICENSE_NOT_FOUND 888

const char *applicationVersion = "1.0";
const char *applicationName = "OpenToonz";
const char *rootVarName = "TOONZROOT";
const char *systemVarPrefix = "TOONZ";

namespace
{

//--------------------------------------------------------------------

void doesExist(const TFilePath &fp)
{
	string msg;
	TFilePath path = fp.getParentDir() + (fp.getName() + "." + fp.getDottedType());
	if (TSystem::doesExistFileOrLevel(fp) || TSystem::doesExistFileOrLevel(path)) {
		msg = "File " + fp.getLevelName() + " already exists:";
		cout << endl
			 << msg << endl;
		char answer = ' ';
		while (answer != 'Y' && answer != 'N' && answer != 'y' && answer != 'n') {
			msg = "do you want to replace it? [Y/N] ";
			cout << msg;
			cin >> answer;
			if (answer == 'N' || answer == 'n') {
				msg = "Conversion aborted.";
				cout << endl
					 << msg << endl;
				exit(1);
			}
		}
	}
}

//--------------------------------------------------------------------

//Ritorna un vettore contenete i soli TFrameId corrispondenti al range inserito dall'utente
vector<TFrameId> getFrameIds(const RangeQualifier &range, const TLevelP &level)
{
	string msg;
	TFrameId r0, r1, lastFrame;
	TLevel::Iterator begin = level->begin();
	TLevel::Iterator end = level->end();
	end--;
	lastFrame = end->first;
	vector<TFrameId> frames;
	if (range.isSelected()) {
		r0 = TFrameId(range.getFrom());
		r1 = TFrameId(range.getTo());
		if (r0 > r1) {
			TFrameId app = r0;
			r0 = r1;
			r1 = app;
		}
	} else {
		r0 = begin->first;
		r1 = end->first;
	}
	//cerco il primo TFrameId
	TLevel::Iterator it = begin;
	if (r0 <= end->first)
		while (it->first < r0)
			++it;
	while (it != level->end() && r1 >= it->first) {
		//Riempio il vettore fino all'ultimo TFrameId che mi serve
		frames.push_back(it->first);
		++it;
	}
	return frames;
}

//--------------------------------------------------------------------

void convertFromCM(const TLevelReaderP &lr, const TPaletteP &plt, const TLevelWriterP &lw, const vector<TFrameId> &frames,
				   const TAffine &aff, const TRop::ResampleFilterType &resType)
{

	TDimension dim(0, 0); //Serve per controllare che non ci siano frame di diverse dimensioni
	for (int i = 0; i < (int)frames.size(); i++) {
		try {
			TImageReaderP ir = lr->getFrameReader(frames[i]);
			TImageP img = ir->load();
			TToonzImageP toonzImage(img);
			double xdpi, ydpi;
			toonzImage->getDpi(xdpi, ydpi);
			assert(toonzImage);
			if (toonzImage) {
				TRasterCM32P rasCMImage = toonzImage->getRaster();
				if (i == 0)
					dim = rasCMImage->getSize();
				else if (dim != rasCMImage->getSize()) {
					//dimensioni diverse dei frame
					string msg = "Cannot continue to convert: not valid level!";
					cout << msg << endl;
					exit(1);
				}
				TRaster32P ras(convert(aff * convert(rasCMImage->getBounds())).getSize());
				if (!aff.isIdentity())
					TRop::resample(ras, rasCMImage, plt, aff, resType);
				else
					TRop::convert(ras, rasCMImage, plt);
				TRasterImageP rasImage(ras);
				rasImage->setDpi(xdpi, ydpi);
				TImageWriterP iw = lw->getFrameWriter(frames[i]);
				iw->save(rasImage);
			}
		} catch (...) {
			string msg = "Frame " + toString(frames[i].getNumber()) + ": conversion failed!";
			cout << msg << endl;
		}
	}
}

//--------------------------------------------------------------------

void convertFromVI(const TLevelReaderP &lr, const TPaletteP &plt, const TLevelWriterP &lw, const vector<TFrameId> &frames,
				   const TRop::ResampleFilterType &resType, int width)
{
	int i;
	vector<TVectorImageP> images;
	TRectD maxBbox;
	TAffine aff;
	for (i = 0; i < (int)frames.size(); i++) { //trovo la bbox che possa contenere tutte le immagini
		try {
			TImageReaderP ir = lr->getFrameReader(frames[i]);
			TVectorImageP img = ir->load();
			images.push_back(img);
			maxBbox += img->getBBox();
		} catch (...) {
			string msg = "Frame " + toString(frames[i].getNumber()) + ": conversion failed!";
			cout << msg << endl;
		}
	}
	maxBbox = maxBbox.enlarge(2);
	if (width) //calcolo l'affine
		aff = TScale((double)width / maxBbox.getLx());
	maxBbox = aff * maxBbox;

	for (i = 0; i < (int)images.size(); i++) {
		try {
			TVectorImageP vectorImage = images[i];
			assert(vectorImage);
			if (vectorImage) {
				//faccio il render dell'immagine
				vectorImage->transform(aff, true);
				const TVectorRenderData rd(TTranslation(-maxBbox.getP00()), TRect(), plt.getPointer(), 0, true, true);
				TOfflineGL *glContext = new TOfflineGL(convert(maxBbox).getSize());
				glContext->clear(TPixel32::Transparent);
				glContext->draw(vectorImage, rd);
				TRaster32P rasImage = (glContext->getRaster());
				TImageWriterP iw = lw->getFrameWriter(frames[i]);
				iw->save(TRasterImageP(rasImage));
			}
		} catch (...) {
			string msg = "Frame " + frames[i].expand() + ": conversion failed!";
			cout << msg << endl;
		}
	}
}

//-----------------------------------------------------------------------

void convertFromFullRaster(const TLevelReaderP &lr, const TLevelWriterP &lw, const vector<TFrameId> &frames,
						   const TAffine &aff, const TRop::ResampleFilterType &resType)
{
	for (int i = 0; i < (int)frames.size(); i++) {
		try {
			TImageReaderP ir = lr->getFrameReader(frames[i]);
			TRasterImageP img = ir->load();
			TRaster32P raster(convert(aff * img->getBBox()).getSize());
			if (!aff.isIdentity())
				TRop::resample(raster, img->getRaster(), aff, resType);
			else {
				if ((TRaster32P)img->getRaster())
					raster = img->getRaster();
				else
					TRop::convert(raster, img->getRaster());
			}
			TImageWriterP iw = lw->getFrameWriter(frames[i]);
			iw->save(TRasterImageP(raster));
		} catch (...) {
			string msg = "Frame " + frames[i].expand() + ": conversion failed!";
			cout << msg << endl;
		}
	}
}

//-----------------------------------------------------------------------

void convertFromFullRasterToCm(const TLevelReaderP &lr, const TLevelWriterP &lw, const vector<TFrameId> &frames,
							   const TAffine &aff, const TRop::ResampleFilterType &resType)
{
	TPalette *plt = new TPalette();

	for (int i = 0; i < (int)frames.size(); i++) {
		try {
			TImageReaderP ir = lr->getFrameReader(frames[i]);
			TRasterImageP img = ir->load();
			double dpix, dpiy;
			img->getDpi(dpix, dpiy);
			if (dpix == 0 && dpiy == 0)
				dpix = dpiy = Preferences::instance()->getDefLevelDpi();
			TRasterCM32P raster(convert(aff * img->getBBox()).getSize());

			if (!aff.isIdentity()) {
				TRaster32P raux(raster->getSize());
				TRop::resample(raux, img->getRaster(), aff, resType);
				TRop::convert(raster, raux);
			} else
				TRop::convert(raster, img->getRaster());

			TImageWriterP iw = lw->getFrameWriter(frames[i]);
			TToonzImageP outimg(raster, raster->getBounds());
			outimg->setDpi(dpix, dpiy);
			outimg->setPalette(plt);
			iw->save(outimg);

		} catch (...) {
			string msg = "Frame " + frames[i].expand() + ": conversion failed!";
			cout << msg << endl;
		}
	}

	TFilePath pltPath = lw->getFilePath().withNoFrame().withType("tpl");
	if (TSystem::touchParentDir(pltPath)) {
		if (TSystem::doesExistFileOrLevel(pltPath))
			TSystem::removeFileOrLevel(pltPath);
		TOStream os(pltPath);
		os << plt;
	}
}
//-----------------------------------------------------------------------

void convert(const TFilePath &source, const TFilePath &dest, const RangeQualifier &range,
			 const IntQualifier &width, TPropertyGroup *prop,
			 const TRenderSettings::ResampleQuality &resQuality)
{
	string msg;
	//Carico le informazione del livello
	TLevelReaderP lr(source);
	TLevelP level = lr->loadInfo();

	//Trovo i TFrameId corrispondenti al range
	vector<TFrameId> frames = getFrameIds(range, level);

	doesExist(dest);

	msg = "Level loaded";
	cout << msg << endl;
	msg = "Conversion in progress: wait please...";
	cout << msg << endl;

	TAffine aff;
	if (width.isSelected()) {
		//calcolo un affine per fare la resample
		int imgLx = lr->getImageInfo()->m_lx;
		aff = TScale((double)width / (double)imgLx);
	}

	//setto il FilterResempleType giusto
	TRop::ResampleFilterType resType;
	if (resQuality == TRenderSettings::StandardResampleQuality)
		resType = TRop::Triangle;
	else if (resQuality == TRenderSettings::ImprovedResampleQuality)
		resType = TRop::Hann2;
	else if (resQuality == TRenderSettings::HighResampleQuality)
		resType = TRop::Hamming3;
	else if (resQuality == TRenderSettings::Triangle_FilterResampleQuality)
		resType = TRop::Triangle;
	else if (resQuality == TRenderSettings::Mitchell_FilterResampleQuality)
		resType = TRop::Mitchell;
	else if (resQuality == TRenderSettings::Cubic5_FilterResampleQuality)
		resType = TRop::Cubic5;
	else if (resQuality == TRenderSettings::Cubic75_FilterResampleQuality)
		resType = TRop::Cubic75;
	else if (resQuality == TRenderSettings::Cubic1_FilterResampleQuality)
		resType = TRop::Cubic1;
	else if (resQuality == TRenderSettings::Hann2_FilterResampleQuality)
		resType = TRop::Hann2;
	else if (resQuality == TRenderSettings::Hann3_FilterResampleQuality)
		resType = TRop::Hann3;
	else if (resQuality == TRenderSettings::Hamming2_FilterResampleQuality)
		resType = TRop::Hamming2;
	else if (resQuality == TRenderSettings::Hamming3_FilterResampleQuality)
		resType = TRop::Hamming3;
	else if (resQuality == TRenderSettings::Lanczos2_FilterResampleQuality)
		resType = TRop::Lanczos2;
	else if (resQuality == TRenderSettings::Lanczos3_FilterResampleQuality)
		resType = TRop::Lanczos3;
	else if (resQuality == TRenderSettings::Gauss_FilterResampleQuality)
		resType = TRop::Gauss;
	else if (resQuality == TRenderSettings::ClosestPixel_FilterResampleQuality)
		resType = TRop::ClosestPixel;
	else if (resQuality == TRenderSettings::Bilinear_FilterResampleQuality)
		resType = TRop::Bilinear;

	string ext = source.getType();
	TLevelWriterP lw(dest, prop);
	if (ext != "tlv" && ext != "pli") {
		if (dest.getType() == "tlv")
			convertFromFullRasterToCm(lr, lw, frames, aff, resType);
		else
			convertFromFullRaster(lr, lw, frames, aff, resType);
	} else if (ext == "tlv") //ToonzImage
		convertFromCM(lr, level->getPalette(), lw, frames, aff, resType);
	else if (ext == "pli") //VectorImage
		convertFromVI(lr, level->getPalette(), lw, frames, resType, width.getValue());
}

} //namespace

//------------------------------------------------------------------------

int main(int argc, char *argv[])
{
	TEnv::setApplication(applicationName, applicationVersion);
	TEnv::setRootVarName(rootVarName);
	TEnv::setSystemVarPrefix(systemVarPrefix);
	TFilePath fp = TEnv::getStuffDir();

	string msg;
	//Inizializzo i qualificatori
	TCli::FilePathArgument srcName("srcName", "Source file");
	TCli::FilePathArgument dstName("dstName", "Target file");
	FilePathQualifier tnzName("-s sceneName", "Scene file");
	RangeQualifier range;
	IntQualifier width("-w width", "Image width");

	Usage usage(argv[0]);
	usage.add(srcName + dstName + width + tnzName + range);
	if (!usage.parse(argc, argv))
		exit(1);

	try {
		Tiio::defineStd();
		//TPluginManager::instance()->loadStandardPlugins();

		TSystem::hasMainLoop(false);
		TPropertyGroup *prop = 0;
		initImageIo();
		TRenderSettings::ResampleQuality resQuality = TRenderSettings::StandardResampleQuality;

		TFilePath dstFilePath = dstName.getValue();
		TFilePath srcFilePath = srcName.getValue();
		if (!TSystem::doesExistFileOrLevel(srcFilePath)) {
			msg = srcFilePath.getLevelName() + " level doesn't exist.";
			cout << endl
				 << msg << endl;
			exit(1);
		}
		msg = "Loading " + srcFilePath.getLevelName();
		cout << endl
			 << msg << endl;

		string ext = dstFilePath.getType();
		//controllo che ci sia un'estensione
		if (ext == "") {
			ext = toString(dstFilePath.getWideString());
			if (ext == "") {
				msg = "Invalid extension!";
				cout << msg << endl;
				exit(1);
			}
		}
		if (dstFilePath.getParentDir().isEmpty()) //ho specificato solo l'estensione
			dstFilePath = srcFilePath.getParentDir() + (srcFilePath.getName() + "." + ext);
		if (tnzName.isSelected()) {
			//Devo prendermi i settaggi degli "output setting" dalla scena!
			TFilePath tnzFilePath = tnzName.getValue();
			if (tnzFilePath.getType() != "tnz") {
				msg = "Invalid scene file: conversion terminated!";
				cout << msg << endl;
				exit(1);
			}

			if (!TSystem::doesExistFileOrLevel(tnzFilePath))
				return false;
			ToonzScene *scene = new ToonzScene();
			try {
				scene->loadTnzFile(tnzFilePath);
			} catch (...) {
				string msg;
				msg = "There were problems loading the scene " + toString(srcFilePath.getWideString()) +
					  ".\n Some files may be missing.";
				cout << msg << endl;
				//return false;
			}

			if (scene) {
				resQuality = scene->getProperties()->getOutputProperties()->getRenderSettings().m_quality;
				prop = scene->getProperties()->getOutputProperties()->getFileFormatProperties(ext);
			} else {
				msg = "Invalid scene file: conversion terminated!";
				cout << msg << endl;
				exit(1);
			}
		}
		if (ext != "3gp" && ext != "pli") {
			//assert(ext!="3gp" && ext!="pli" && ext!="tlv");
			convert(srcFilePath, dstFilePath, range, width, prop, resQuality);
		} else {
			msg = "Cannot convert to ." + ext + " format.";
			cout << msg << endl;
			exit(1);
		}
	} catch (TException &e) {
		msg = "Untrapped exception: " + toString(e.getMessage());
		cout << msg << endl;
		return -1;
	}
	msg = "Conversion terminated!";
	cout << endl
		 << msg << endl;
	return 0;
}