3316edbab0
Qt documentation for QOffscreenSurface: Due to the fact that QOffscreenSurface is backed by a QWindow on some platforms, cross-platform applications must ensure that create() is only called on the main (GUI) thread. The QOffscreenSurface is then safe to be used with makeCurrent() on other threads, but the initialization and destruction must always happen on the main (GUI) thread.
467 lines
15 KiB
C++
467 lines
15 KiB
C++
|
|
|
|
// 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"
|
|
|
|
#if defined(LINUX)
|
|
#include <QGuiApplication>
|
|
#endif
|
|
|
|
using namespace std;
|
|
using namespace TCli;
|
|
|
|
typedef ArgumentT<TFilePath> FilePathArgument;
|
|
typedef QualifierT<TFilePath> FilePathQualifier;
|
|
|
|
#define RENDER_LICENSE_NOT_FOUND 888
|
|
|
|
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 " + std::to_string(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 " + std::to_string(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[]) {
|
|
#if defined(LINUX)
|
|
QGuiApplication app(argc, argv);
|
|
#endif
|
|
|
|
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 = ::to_string(dstFilePath);
|
|
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 " +
|
|
::to_string(srcFilePath) + ".\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: " + ::to_string(e.getMessage());
|
|
cout << msg << endl;
|
|
return -1;
|
|
}
|
|
msg = "Conversion terminated!";
|
|
cout << endl << msg << endl;
|
|
return 0;
|
|
}
|