// 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 #endif using namespace std; using namespace TCli; typedef ArgumentT FilePathArgument; typedef QualifierT 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 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 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 &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 &frames, const TRop::ResampleFilterType &resType, int width) { int i; vector 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 &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 &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 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 -1; 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 != "pli") { 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; }