diff --git a/toonz/sources/image/CMakeLists.txt b/toonz/sources/image/CMakeLists.txt index 4b784f1..d0a19c2 100644 --- a/toonz/sources/image/CMakeLists.txt +++ b/toonz/sources/image/CMakeLists.txt @@ -20,6 +20,10 @@ set(HEADERS ../include/tnzimage.h mov/tiio_mov_proxy.h 3gp/tiio_3gp_proxy.h + ffmpeg/tiio_gif.h + ffmpeg/tiio_webm.h + ffmpeg/tiio_mp4.h + ffmpeg/tiio_ffmpeg.h mesh/tiio_mesh.h ) @@ -47,6 +51,10 @@ set(SOURCES tzl/tiio_tzl.cpp mov/tiio_mov_proxy.cpp 3gp/tiio_3gp_proxy.cpp + ffmpeg/tiio_gif.cpp + ffmpeg/tiio_webm.cpp + ffmpeg/tiio_mp4.cpp + ffmpeg/tiio_ffmpeg.cpp mesh/tiio_mesh.cpp ) @@ -114,11 +122,9 @@ if(WIN32) endif() endif() -if(PLATFORM EQUAL 32) - _find_toonz_library(TNZLIBS "tnzcore;tnzbase") -else() - _find_toonz_library(TNZLIBS "tnzcore;tnzbase;toonzlib") -endif() + +_find_toonz_library(TNZLIBS "tnzcore;tnzbase;toonzlib") + if(WIN32) set(EXTRA_LIBS @@ -152,4 +158,4 @@ else() ) endif() -target_link_libraries(image Qt5::Core Qt5::Network ${Z_LIB} ${GLUT_LIB} ${GL_LIB} ${JPEG_LIB} ${TIFF_LIB} ${PNG_LIB} ${EXTRA_LIBS}) +target_link_libraries(image Qt5::Core Qt5::Gui Qt5::Network ${Z_LIB} ${GLUT_LIB} ${GL_LIB} ${JPEG_LIB} ${TIFF_LIB} ${PNG_LIB} ${EXTRA_LIBS}) diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp new file mode 100644 index 0000000..33ac88b --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp @@ -0,0 +1,418 @@ +#include "tiio_ffmpeg.h" +#include "../toonz/tapp.h" +#include "tsystem.h" +#include "tsound.h" + +#include +#include +#include +#include "toonz/preferences.h" +#include "toonz/toonzfolders.h" + +Ffmpeg::Ffmpeg() { + m_ffmpegPath = Preferences::instance()->getFfmpegPath(); + m_ffmpegTimeout = Preferences::instance()->getFfmpegTimeout() * 1000; + std::string strPath = m_ffmpegPath.toStdString(); + m_intermediateFormat = "png"; +} +Ffmpeg::~Ffmpeg() {} + +bool Ffmpeg::checkFfmpeg() { + // check the user defined path in preferences first + QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; +#if defined(_WIN32) + path = path + ".exe"; +#endif + if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; + + // check the OpenToonz root directory next + path = QDir::currentPath() + "/ffmpeg"; +#if defined(_WIN32) + path = path + ".exe"; +#endif + if (TSystem::doesExistFileOrLevel(TFilePath(path))) { + Preferences::instance()->setFfmpegPath(QDir::currentPath().toStdString()); + return true; + } + + // give up + return false; +} + +bool Ffmpeg::checkFfprobe() { + // check the user defined path in preferences first + QString path = Preferences::instance()->getFfmpegPath() + "/ffprobe"; +#if defined(_WIN32) + path = path + ".exe"; +#endif + if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; + + // check the OpenToonz root directory next + path = QDir::currentPath() + "/ffprobe"; +#if defined(_WIN32) + path = path + ".exe"; +#endif + if (TSystem::doesExistFileOrLevel(TFilePath(path))) { + Preferences::instance()->setFfmpegPath(QDir::currentPath().toStdString()); + return true; + } + + // give up + return false; +} + +bool Ffmpeg::checkFormat(std::string format) { + QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; +#if defined(_WIN32) + path = path + ".exe"; +#endif + QStringList args; + args << "-formats"; + QProcess ffmpeg; + ffmpeg.start(path, args); + ffmpeg.waitForFinished(); + QString results = ffmpeg.readAllStandardError(); + results += ffmpeg.readAllStandardOutput(); + ffmpeg.close(); + std::string strResults = results.toStdString(); + std::string::size_type n; + n = strResults.find(format); + if (n != std::string::npos) return true; + else return false; +} + + + +TFilePath Ffmpeg::getFfmpegCache() { + QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString(); + if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/ffmpeg"))) { + TSystem::mkDir(TFilePath(cacheRoot + "/ffmpeg")); + } + std::string ffmpegPath = + TFilePath(cacheRoot + "/ffmpeg").getQString().toStdString(); + return TFilePath(cacheRoot + "/ffmpeg"); +} + +void Ffmpeg::setFrameRate(double fps) { m_frameRate = fps; } + +void Ffmpeg::setPath(TFilePath path) { m_path = path; } + +void Ffmpeg::createIntermediateImage(const TImageP &img, int frameIndex) { + QString tempPath = m_path.getQString() + "tempOut" + + QString::number(frameIndex) + "." + m_intermediateFormat; + std::string saveStatus = ""; + TRasterImageP tempImage(img); + TRasterImage *image = (TRasterImage *)tempImage->cloneImage(); + + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + m_bpp = image->getRaster()->getPixelSize(); + int totalBytes = m_lx * m_ly * m_bpp; + image->getRaster()->yMirror(); + + // lock raster to get data + image->getRaster()->lock(); + void *buffin = image->getRaster()->getRawData(); + assert(buffin); + void *buffer = malloc(totalBytes); + memcpy(buffer, buffin, totalBytes); + + image->getRaster()->unlock(); + + // create QImage save format + QByteArray ba = m_intermediateFormat.toUpper().toLatin1(); + const char *format = ba.data(); + + QImage *qi = new QImage((uint8_t *)buffer, m_lx, m_ly, QImage::Format_ARGB32); + qi->save(tempPath, format, -1); + free(buffer); + m_cleanUpList.push_back(tempPath); + m_frameCount++; + delete qi; + delete image; +} + +void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, + bool includesInPath, bool includesOutPath, + bool overWriteFiles) { + QString tempName = "tempOut%d." + m_intermediateFormat; + tempName = m_path.getQString() + tempName; + + QStringList args; + args = args + preIArgs; + if (!includesInPath) { // NOTE: if including the in path, it needs to be in + // the preIArgs argument. + args << "-i"; + args << tempName; + } + if (m_hasSoundTrack) args = args + m_audioArgs; + args = args + postIArgs; + if (overWriteFiles && !includesOutPath) { // if includesOutPath is true, you + // need to include the overwrite in + // your postIArgs. + args << "-y"; + } + if (!includesOutPath) { + args << m_path.getQString(); + } + + // write the file + QProcess ffmpeg; + ffmpeg.start(m_ffmpegPath + "/ffmpeg", args); + ffmpeg.waitForFinished(m_ffmpegTimeout); + QString results = ffmpeg.readAllStandardError(); + results += ffmpeg.readAllStandardOutput(); + int exitCode = ffmpeg.exitCode(); + ffmpeg.close(); + std::string strResults = results.toStdString(); +} + +QString Ffmpeg::runFfprobe(QStringList args) { + QProcess ffmpeg; + ffmpeg.start(m_ffmpegPath + "/ffprobe", args); + ffmpeg.waitForFinished(m_ffmpegTimeout); + QString results = ffmpeg.readAllStandardError(); + results += ffmpeg.readAllStandardOutput(); + int exitCode = ffmpeg.exitCode(); + ffmpeg.close(); + std::string strResults = results.toStdString(); + return results; +} + +void Ffmpeg::saveSoundTrack(TSoundTrack *st) { + m_sampleRate = st->getSampleRate(); + m_channelCount = st->getChannelCount(); + m_bitsPerSample = st->getBitPerSample(); + // LONG count = st->getSampleCount(); + int bufSize = st->getSampleCount() * st->getSampleSize(); + const UCHAR *buffer = st->getRawData(); + + m_audioPath = m_path.getQString() + "tempOut.raw"; + m_audioFormat = "s" + QString::number(m_bitsPerSample); + if (m_bitsPerSample > 8) m_audioFormat = m_audioFormat + "le"; + std::string strPath = m_audioPath.toStdString(); + + QByteArray data; + data.insert(0, (char *)buffer, bufSize); + + QFile file(m_audioPath); + file.open(QIODevice::WriteOnly); + file.write(data); + file.close(); + m_hasSoundTrack = true; + + m_audioArgs << "-f"; + m_audioArgs << m_audioFormat; + m_audioArgs << "-ar"; + m_audioArgs << QString::number(m_sampleRate); + m_audioArgs << "-ac"; + m_audioArgs << QString::number(m_channelCount); + m_audioArgs << "-i"; + m_audioArgs << m_audioPath; + + // add file to framesWritten for cleanup + m_cleanUpList.push_back(m_audioPath); +} + +bool Ffmpeg::checkFilesExist() { + QString ffmpegCachePath = getFfmpegCache().getQString(); + QString tempPath = ffmpegCachePath + "//" + + QString::fromStdString(m_path.getName()) + + QString::fromStdString(m_path.getType()) + "In0001." + + m_intermediateFormat; + if (TSystem::doesExistFileOrLevel(TFilePath(tempPath))) { + return true; + } else + return false; +} + +ffmpegFileInfo Ffmpeg::getInfo() { + QString ffmpegCachePath = getFfmpegCache().getQString(); + QString tempPath = ffmpegCachePath + "//" + + QString::fromStdString(m_path.getName()) + + QString::fromStdString(m_path.getType()) + ".txt"; + if (QFile::exists(tempPath)) { + QFile infoText(tempPath); + infoText.open(QIODevice::ReadOnly); + QByteArray ba = infoText.readAll(); + infoText.close(); + QString text = QString::fromStdString(ba.toStdString()); + m_lx = text.split(" ")[0].toInt(); + m_ly = text.split(" ")[1].toInt(); + m_frameRate = text.split(" ")[2].toDouble(); + m_frameCount = text.split(" ")[3].toInt(); + } else { + QFile infoText(tempPath); + getSize(); + getFrameRate(); + getFrameCount(); + infoText.open(QIODevice::WriteOnly); + std::string infoToWrite = + std::to_string(m_lx) + " " + std::to_string(m_ly) + " " + + std::to_string(m_frameRate) + " " + std::to_string(m_frameCount); + int infoLength = infoToWrite.length(); + infoText.write(infoToWrite.c_str(), infoLength); + infoText.close(); + } + ffmpegFileInfo info; + info.m_lx = m_lx; + info.m_ly = m_ly; + info.m_frameRate = m_frameRate; + info.m_frameCount = m_frameCount; + return info; +} +TRasterImageP Ffmpeg::getImage(int frameIndex) { + QString ffmpegCachePath = getFfmpegCache().getQString(); + QString tempPath = ffmpegCachePath + "//" + + QString::fromStdString(m_path.getName()) + + QString::fromStdString(m_path.getType()); + std::string tmpPath = tempPath.toStdString(); + // QString tempPath= m_path.getQString(); + QString number = QString("%1").arg(frameIndex, 4, 10, QChar('0')); + QString tempName = "In" + number + ".png"; + tempName = tempPath + tempName; + + // for debugging + std::string strPath = tempName.toStdString(); + if (TSystem::doesExistFileOrLevel(TFilePath(tempName))) { + QImage *temp = new QImage(tempName, "PNG"); + if (temp) { + QImage tempToo = temp->convertToFormat(QImage::Format_ARGB32); + delete temp; + const UCHAR *bits = tempToo.bits(); + + TRasterPT ret; + ret.create(m_lx, m_ly); + ret->lock(); + memcpy(ret->getRawData(), bits, m_lx * m_ly * 4); + ret->unlock(); + ret->yMirror(); + return TRasterImageP(ret); + } + } else + return TRasterImageP(); +} + +double Ffmpeg::getFrameRate() { + if (m_frameCount > 0) { + QStringList fpsArgs; + fpsArgs << "-v"; + fpsArgs << "error"; + fpsArgs << "-select_streams"; + fpsArgs << "v:0"; + fpsArgs << "-show_entries"; + fpsArgs << "stream=avg_frame_rate"; + fpsArgs << "-of"; + fpsArgs << "default=noprint_wrappers=1:nokey=1"; + fpsArgs << m_path.getQString(); + QString fpsResults = runFfprobe(fpsArgs); + + int fpsNum = fpsResults.split("/")[0].toInt(); + int fpsDen = fpsResults.split("/")[1].toInt(); + if (fpsDen > 0) { + m_frameRate = fpsNum / fpsDen; + } + } + return m_frameRate; +} + +TDimension Ffmpeg::getSize() { + QStringList sizeArgs; + sizeArgs << "-v"; + sizeArgs << "error"; + sizeArgs << "-of"; + sizeArgs << "flat=s=_"; + sizeArgs << "-select_streams"; + sizeArgs << "v:0"; + sizeArgs << "-show_entries"; + sizeArgs << "stream=height,width"; + sizeArgs << m_path.getQString(); + + QString sizeResults = runFfprobe(sizeArgs); + QStringList split = sizeResults.split("\n"); + m_lx = split[0].split("=")[1].toInt(); + m_ly = split[1].split("=")[1].toInt(); + return TDimension(m_lx, m_ly); +} + +int Ffmpeg::getFrameCount() { + QStringList frameCountArgs; + frameCountArgs << "-v"; + frameCountArgs << "error"; + frameCountArgs << "-count_frames"; + frameCountArgs << "-select_streams"; + frameCountArgs << "v:0"; + frameCountArgs << "-show_entries"; + frameCountArgs << "stream=nb_read_frames"; + frameCountArgs << "-of"; + frameCountArgs << "default=nokey=1:noprint_wrappers=1"; + frameCountArgs << m_path.getQString(); + + QString frameResults = runFfprobe(frameCountArgs); + m_frameCount = frameResults.toInt(); + return m_frameCount; +} + +void Ffmpeg::getFramesFromMovie(int frame) { + QString ffmpegCachePath = getFfmpegCache().getQString(); + QString tempPath = ffmpegCachePath + "//" + + QString::fromStdString(m_path.getName()) + + QString::fromStdString(m_path.getType()); + std::string tmpPath = tempPath.toStdString(); + QString tempName = "In%04d." + m_intermediateFormat; + tempName = tempPath + tempName; + QString tempStart; + if (frame == -1) { + tempStart = "In0001." + m_intermediateFormat; + tempStart = tempPath + tempStart; + } else { + QString number = QString("%1").arg(frame, 4, 10, QChar('0')); + tempStart = tempPath + "In" + number + "." + m_intermediateFormat; + } + QString tempBase = tempPath + "In"; + QString addToDelete; + if (!TSystem::doesExistFileOrLevel(TFilePath(tempStart))) { + // for debugging + std::string strPath = tempName.toStdString(); + + QStringList preIFrameArgs; + QStringList postIFrameArgs; + // frameArgs << "-accurate_seek"; + // frameArgs << "-ss"; + // frameArgs << "0" + QString::number(frameIndex / m_info->m_frameRate); + preIFrameArgs << "-i"; + preIFrameArgs << m_path.getQString(); + postIFrameArgs << "-y"; + postIFrameArgs << "-f"; + postIFrameArgs << "image2"; + + postIFrameArgs << tempName; + + runFfmpeg(preIFrameArgs, postIFrameArgs, true, true, true); + + for (int i = 1; i <= m_frameCount; i++) { + QString number = QString("%1").arg(i, 4, 10, QChar('0')); + addToDelete = tempBase + number + "." + m_intermediateFormat; + std::string delPath = addToDelete.toStdString(); + // addToCleanUp(addToDelete); + } + } +} + +void Ffmpeg::addToCleanUp(QString path) { + if (TSystem::doesExistFileOrLevel(TFilePath(path))) { + m_cleanUpList.push_back(path); + } +} + +void Ffmpeg::cleanUpFiles() { + for (QString path : m_cleanUpList) { + if (TSystem::doesExistFileOrLevel(TFilePath(path))) { + TSystem::deleteFile(TFilePath(path)); + } + } +} + +void Ffmpeg::disablePrecompute() { + Preferences::instance()->setPrecompute(false); +} \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.h b/toonz/sources/image/ffmpeg/tiio_ffmpeg.h new file mode 100644 index 0000000..24aa93f --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.h @@ -0,0 +1,57 @@ +#pragma once + +#ifndef TTIO_FFMPEG_INCLUDED +#define TTIO_FFMPEG_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "trasterimage.h" +#include +#include + +struct ffmpegFileInfo { + int m_lx, m_ly, m_frameCount; + double m_frameRate; +}; + +class Ffmpeg { +public: + Ffmpeg(); + ~Ffmpeg(); + void createIntermediateImage(const TImageP &image, int frameIndex); + void runFfmpeg(QStringList preIArgs, QStringList postIArgs, + bool includesInPath, bool includesOutPath, + bool overWriteFiles); + void runFfmpeg(QStringList preIArgs, QStringList postIArgs, TFilePath path); + QString runFfprobe(QStringList args); + void cleanUpFiles(); + void addToCleanUp(QString); + void setFrameRate(double fps); + void setPath(TFilePath path); + void saveSoundTrack(TSoundTrack *st); + bool checkFilesExist(); + static bool checkFfmpeg(); + static bool checkFfprobe(); + static bool checkFormat(std::string format); + double getFrameRate(); + TDimension getSize(); + int getFrameCount(); + void getFramesFromMovie(int frame = -1); + TRasterImageP getImage(int frameIndex); + TFilePath getFfmpegCache(); + ffmpegFileInfo getInfo(); + void disablePrecompute(); + +private: + QString m_intermediateFormat, m_ffmpegPath, m_audioPath, m_audioFormat; + int m_frameCount = 0, m_lx, m_ly, m_bpp, m_bitsPerSample, m_channelCount, + m_ffmpegTimeout = 30000; + double m_frameRate = 24.0; + bool m_ffmpegExists = false, m_ffprobeExists = false, m_hasSoundTrack = false; + TFilePath m_path; + QVector m_cleanUpList; + QStringList m_audioArgs; + TUINT32 m_sampleRate; +}; + +#endif diff --git a/toonz/sources/image/ffmpeg/tiio_gif.cpp b/toonz/sources/image/ffmpeg/tiio_gif.cpp new file mode 100644 index 0000000..1db30cb --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_gif.cpp @@ -0,0 +1,251 @@ + +#include "tsystem.h" +#include "tiio_gif.h" +#include "trasterimage.h" +#include "timageinfo.h" +#include + +//=========================================================== +// +// TImageWriterGif +// +//=========================================================== + +class TImageWriterGif : public TImageWriter { +public: + int m_frameIndex; + + TImageWriterGif(const TFilePath &path, int frameIndex, TLevelWriterGif *lwg) + : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { + m_lwg->addRef(); + } + ~TImageWriterGif() { m_lwg->release(); } + + bool is64bitOutputSupported() override { return false; } + void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } + +private: + TLevelWriterGif *m_lwg; +}; + +//=========================================================== +// +// TLevelWriterGif; +// +//=========================================================== + +TLevelWriterGif::TLevelWriterGif(const TFilePath &path, TPropertyGroup *winfo) + : TLevelWriter(path, winfo) { + if (!m_properties) m_properties = new Tiio::GifWriterProperties(); + std::string scale = m_properties->getProperty("Scale") + ->getValueAsString(); + m_scale = QString::fromStdString(scale).toInt(); + TBoolProperty *looping = + (TBoolProperty *)m_properties->getProperty("Looping"); + m_looping = looping->getValue(); + TBoolProperty *palette = + (TBoolProperty *)m_properties->getProperty("Generate Palette"); + m_palette = palette->getValue(); + ffmpegWriter = new Ffmpeg(); + ffmpegWriter->setPath(m_path); + // m_frameCount = 0; + if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path); +} + +//----------------------------------------------------------- + +TLevelWriterGif::~TLevelWriterGif() { + QStringList preIArgs; + QStringList postIArgs; + QStringList palettePreIArgs; + QStringList palettePostIArgs; + + int outLx = m_lx; + int outLy = m_ly; + + // set scaling + outLx = m_lx * m_scale / 100; + outLy = m_ly * m_scale / 100; + //ffmpeg doesn't like resolutions that aren't divisible by 2. + if (outLx % 2 != 0) outLx++; + if (outLy % 2 != 0) outLy++; + + QString palette; + QString filters = "scale=" + QString::number(outLx) + ":-1:flags=lanczos"; + QString paletteFilters = filters + " [x]; [x][1:v] paletteuse"; + if (m_palette) { + palette = m_path.getQString() + "palette.png"; + palettePreIArgs << "-v"; + palettePreIArgs << "warning"; + + palettePostIArgs << "-vf"; + palettePostIArgs << filters + ",palettegen"; + palettePostIArgs << palette; + + // write the palette + ffmpegWriter->runFfmpeg(palettePreIArgs, palettePostIArgs, false, true, + true); + ffmpegWriter->addToCleanUp(palette); + } + + preIArgs << "-v"; + preIArgs << "warning"; + preIArgs << "-r"; + preIArgs << QString::number(m_frameRate); + if (m_palette) { + postIArgs << "-i"; + postIArgs << palette; + postIArgs << "-lavfi"; + postIArgs << paletteFilters; + } + else { + postIArgs << "-lavfi"; + postIArgs << filters; + } + + if (!m_looping) { + postIArgs << "-loop"; + postIArgs << "-1"; + } + + std::string outPath = m_path.getQString().toStdString(); + + ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true); + ffmpegWriter->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriterGif::getFrameWriter(TFrameId fid) { + // if (IOError != 0) + // throw TImageException(m_path, buildGifExceptionString(IOError)); + if (fid.getLetter() != 0) return TImageWriterP(0); + int index = fid.getNumber(); + TImageWriterGif *iwg = new TImageWriterGif(m_path, index, this); + return TImageWriterP(iwg); +} + +//----------------------------------------------------------- +void TLevelWriterGif::setFrameRate(double fps) { + // m_fps = fps; + m_frameRate = fps; + ffmpegWriter->setFrameRate(fps); +} + +void TLevelWriterGif::saveSoundTrack(TSoundTrack *st) { return; } + +//----------------------------------------------------------- + +void TLevelWriterGif::save(const TImageP &img, int frameIndex) { + TRasterImageP image(img); + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + ffmpegWriter->createIntermediateImage(img, frameIndex); +} + +//=========================================================== +// +// TImageReaderGif +// +//=========================================================== + +class TImageReaderGif final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderGif(const TFilePath &path, int index, TLevelReaderGif *lra) + : TImageReader(path), m_lra(lra), m_frameIndex(index) { + m_lra->addRef(); + } + ~TImageReaderGif() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + +private: + TLevelReaderGif *m_lra; + + // not implemented + TImageReaderGif(const TImageReaderGif &); + TImageReaderGif &operator=(const TImageReaderGif &src); +}; + +//=========================================================== +// +// TLevelReaderGif +// +//=========================================================== + +TLevelReaderGif::TLevelReaderGif(const TFilePath &path) + : TLevelReader(path) + +{ + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + ffmpegReader->getFramesFromMovie(); + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; +} +//----------------------------------------------------------- + +TLevelReaderGif::~TLevelReaderGif() { + // ffmpegReader->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TLevelP TLevelReaderGif::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderGif::getFrameReader(TFrameId fid) { + // if (IOError != 0) + // throw TImageException(m_path, buildAVIExceptionString(IOError)); + if (fid.getLetter() != 0) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderGif *irm = new TImageReaderGif(m_path, index, this); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderGif::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderGif::load(int frameIndex) { + return ffmpegReader->getImage(frameIndex); +} + +Tiio::GifWriterProperties::GifWriterProperties() + : m_scale("Scale", 1, 100, 100) + , m_looping("Looping", true) + , m_palette("Generate Palette", true) { + bind(m_scale); + bind(m_looping); + bind(m_palette); +} + +// Tiio::Reader* Tiio::makeGifReader(){ return nullptr; } +// Tiio::Writer* Tiio::makeGifWriter(){ return nullptr; } \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_gif.h b/toonz/sources/image/ffmpeg/tiio_gif.h new file mode 100644 index 0000000..844cf84 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_gif.h @@ -0,0 +1,90 @@ +#pragma once + +#ifndef TTIO_GIF_INCLUDED +#define TTIO_GIF_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "tiio_ffmpeg.h" +//#include "tthreadmessage.h" + +//=========================================================== +// +// TLevelWriterGif +// +//=========================================================== + +class TLevelWriterGif : public TLevelWriter { +public: + TLevelWriterGif(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterGif(); + // FfmpegBridge* ffmpeg; + void setFrameRate(double fps); + + TImageWriterP getFrameWriter(TFrameId fid) override; + void save(const TImageP &image, int frameIndex); + + void saveSoundTrack(TSoundTrack *st); + + static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { + return new TLevelWriterGif(path, winfo); + } + +private: + Ffmpeg *ffmpegWriter; + int m_frameCount, m_lx, m_ly; + // double m_fps; + int m_scale; + bool m_looping = false; + bool m_palette = false; +}; + +//=========================================================== +// +// TLevelReaderGif +// +//=========================================================== + +class TLevelReaderGif final : public TLevelReader { +public: + TLevelReaderGif(const TFilePath &path); + ~TLevelReaderGif(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderGif(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); + // TThread::Mutex m_mutex; + // void *m_decompressedBuffer; +private: + Ffmpeg *ffmpegReader; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + +namespace Tiio { + +//=========================================================================== + +class GifWriterProperties : public TPropertyGroup { +public: + TIntProperty m_scale; + TBoolProperty m_looping; + TBoolProperty m_palette; + GifWriterProperties(); +}; + +//=========================================================================== + +// Tiio::Reader *makeGifReader(); +// Tiio::Writer *makeGifWriter(); + +} // namespace + +#endif diff --git a/toonz/sources/image/ffmpeg/tiio_mp4.cpp b/toonz/sources/image/ffmpeg/tiio_mp4.cpp new file mode 100644 index 0000000..7c136a4 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_mp4.cpp @@ -0,0 +1,220 @@ + +#include "tsystem.h" +#include "tiio_mp4.h" +#include "trasterimage.h" +#include "timageinfo.h" +#include "tsound.h" +#include + +//=========================================================== +// +// TImageWriterMp4 +// +//=========================================================== + +class TImageWriterMp4 : public TImageWriter { +public: + int m_frameIndex; + + TImageWriterMp4(const TFilePath &path, int frameIndex, TLevelWriterMp4 *lwg) + : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { + m_lwg->addRef(); + } + ~TImageWriterMp4() { m_lwg->release(); } + + bool is64bitOutputSupported() override { return false; } + void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } + +private: + TLevelWriterMp4 *m_lwg; +}; + +//=========================================================== +// +// TLevelWriterMp4; +// +//=========================================================== + +TLevelWriterMp4::TLevelWriterMp4(const TFilePath &path, TPropertyGroup *winfo) + : TLevelWriter(path, winfo) { + if (!m_properties) m_properties = new Tiio::Mp4WriterProperties(); + std::string scale = m_properties->getProperty("Scale")->getValueAsString(); + m_scale = QString::fromStdString(scale).toInt(); + std::string quality = m_properties->getProperty("Quality")->getValueAsString(); + m_vidQuality = QString::fromStdString(quality).toInt(); + ffmpegWriter = new Ffmpeg(); + ffmpegWriter->setPath(m_path); + if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path); +} + +//----------------------------------------------------------- + +TLevelWriterMp4::~TLevelWriterMp4() { + // QProcess createMp4; + QStringList preIArgs; + QStringList postIArgs; + + int outLx = m_lx; + int outLy = m_ly; + + // set scaling + if (m_scale != 0) { + outLx = m_lx * m_scale / 100; + outLy = m_ly * m_scale / 100; + } + //ffmpeg doesn't like resolutions that aren't divisible by 2. + if (outLx % 2 != 0) outLx++; + if (outLy % 2 != 0) outLy++; + + // calculate quality (bitrate) + int pixelCount = m_lx * m_ly; + int bitRate = pixelCount / 150; // crude but gets decent values + double quality = m_vidQuality / 100.0; + double tempRate = (double)bitRate * quality; + int finalBitrate = (int)tempRate; + int crf = 51 - (m_vidQuality * 51 / 100); + + preIArgs << "-framerate"; + preIArgs << QString::number(m_frameRate); + + postIArgs << "-pix_fmt"; + postIArgs << "yuv420p"; + postIArgs << "-s"; + postIArgs << QString::number(outLx) + "x" + QString::number(outLy); + postIArgs << "-b"; + postIArgs << QString::number(finalBitrate) + "k"; + + ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true); + ffmpegWriter->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriterMp4::getFrameWriter(TFrameId fid) { + // if (IOError != 0) + // throw TImageException(m_path, buildMp4ExceptionString(IOError)); + if (fid.getLetter() != 0) return TImageWriterP(0); + int index = fid.getNumber(); + TImageWriterMp4 *iwg = new TImageWriterMp4(m_path, index, this); + return TImageWriterP(iwg); +} + +//----------------------------------------------------------- +void TLevelWriterMp4::setFrameRate(double fps) { + m_frameRate = fps; + ffmpegWriter->setFrameRate(fps); +} + +void TLevelWriterMp4::saveSoundTrack(TSoundTrack *st) { + ffmpegWriter->saveSoundTrack(st); +} + +//----------------------------------------------------------- + +void TLevelWriterMp4::save(const TImageP &img, int frameIndex) { + TRasterImageP image(img); + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + ffmpegWriter->createIntermediateImage(img, frameIndex); +} + +//=========================================================== +// +// TImageReaderMp4 +// +//=========================================================== + +class TImageReaderMp4 final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderMp4(const TFilePath &path, int index, TLevelReaderMp4 *lra) + : TImageReader(path), m_lra(lra), m_frameIndex(index) { + m_lra->addRef(); + } + ~TImageReaderMp4() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + +private: + TLevelReaderMp4 *m_lra; + + // not implemented + TImageReaderMp4(const TImageReaderMp4 &); + TImageReaderMp4 &operator=(const TImageReaderMp4 &src); +}; + +//=========================================================== +// +// TLevelReaderMp4 +// +//=========================================================== + +TLevelReaderMp4::TLevelReaderMp4(const TFilePath &path) : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + ffmpegReader->getFramesFromMovie(); + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; +} +//----------------------------------------------------------- + +TLevelReaderMp4::~TLevelReaderMp4() { + // ffmpegReader->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TLevelP TLevelReaderMp4::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderMp4::getFrameReader(TFrameId fid) { + // if (IOError != 0) + // throw TImageException(m_path, buildAVIExceptionString(IOError)); + if (fid.getLetter() != 0) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderMp4 *irm = new TImageReaderMp4(m_path, index, this); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderMp4::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderMp4::load(int frameIndex) { + return ffmpegReader->getImage(frameIndex); +} + +Tiio::Mp4WriterProperties::Mp4WriterProperties() + : m_vidQuality("Quality", 1, 100, 90), m_scale("Scale", 1, 100, 100) { + bind(m_vidQuality); + bind(m_scale); +} + +// Tiio::Reader* Tiio::makeMp4Reader(){ return nullptr; } +// Tiio::Writer* Tiio::makeMp4Writer(){ return nullptr; } \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_mp4.h b/toonz/sources/image/ffmpeg/tiio_mp4.h new file mode 100644 index 0000000..97b4f75 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_mp4.h @@ -0,0 +1,88 @@ +#pragma once + +#ifndef TTIO_MP4_INCLUDED +#define TTIO_MP4_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "tiio_ffmpeg.h" + +//=========================================================== +// +// TLevelWriterMp4 +// +//=========================================================== + +class TLevelWriterMp4 : public TLevelWriter { +public: + TLevelWriterMp4(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterMp4(); + void setFrameRate(double fps); + + TImageWriterP getFrameWriter(TFrameId fid) override; + void save(const TImageP &image, int frameIndex); + + void saveSoundTrack(TSoundTrack *st); + + static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { + return new TLevelWriterMp4(path, winfo); + } + +private: + Ffmpeg *ffmpegWriter; + int m_lx, m_ly; + int m_scale; + int m_vidQuality; + // void *m_buffer; +}; + +//=========================================================== +// +// TLevelReaderMp4 +// +//=========================================================== + +class TLevelReaderMp4 final : public TLevelReader { +public: + TLevelReaderMp4(const TFilePath &path); + ~TLevelReaderMp4(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderMp4(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); + // TThread::Mutex m_mutex; + // void *m_decompressedBuffer; +private: + Ffmpeg *ffmpegReader; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + +namespace Tiio { + +//=========================================================================== + +class Mp4WriterProperties : public TPropertyGroup { +public: + // TEnumProperty m_pixelSize; + // TBoolProperty m_matte; + TIntProperty m_vidQuality; + TIntProperty m_scale; + Mp4WriterProperties(); +}; + +//=========================================================================== + +// Tiio::Reader *makeMp4Reader(); +// Tiio::Writer *makeMp4Writer(); + +} // namespace + +#endif diff --git a/toonz/sources/image/ffmpeg/tiio_webm.cpp b/toonz/sources/image/ffmpeg/tiio_webm.cpp new file mode 100644 index 0000000..cd157e2 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_webm.cpp @@ -0,0 +1,224 @@ + +#include "tiio_webm.h" +#include "tsystem.h" +#include "trasterimage.h" +#include "tsound.h" +#include "timageinfo.h" +#include + +//=========================================================== +// +// TImageWriterWebm +// +//=========================================================== + +class TImageWriterWebm : public TImageWriter { +public: + int m_frameIndex; + + TImageWriterWebm(const TFilePath &path, int frameIndex, TLevelWriterWebm *lwg) + : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { + m_lwg->addRef(); + } + ~TImageWriterWebm() { m_lwg->release(); } + + bool is64bitOutputSupported() override { return false; } + void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } + +private: + TLevelWriterWebm *m_lwg; +}; + +//=========================================================== +// +// TLevelWriterWebm; +// +//=========================================================== + +TLevelWriterWebm::TLevelWriterWebm(const TFilePath &path, TPropertyGroup *winfo) + : TLevelWriter(path, winfo) { + if (!m_properties) m_properties = new Tiio::WebmWriterProperties(); + std::string scale = m_properties->getProperty("Scale")->getValueAsString(); + m_scale = QString::fromStdString(scale).toInt(); + std::string quality = m_properties->getProperty("Quality")->getValueAsString(); + m_vidQuality = QString::fromStdString(quality).toInt(); + ffmpegWriter = new Ffmpeg(); + ffmpegWriter->setPath(m_path); + if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path); +} + +//----------------------------------------------------------- + +TLevelWriterWebm::~TLevelWriterWebm() { + // QProcess createWebm; + QStringList preIArgs; + QStringList postIArgs; + + int outLx = m_lx; + int outLy = m_ly; + + // set scaling + if (m_scale != 0) { + outLx = m_lx * m_scale / 100; + outLy = m_ly * m_scale / 100; + } + //ffmpeg doesn't like resolutions that aren't divisible by 2. + if (outLx % 2 != 0) outLx++; + if (outLy % 2 != 0) outLy++; + + // calculate quality (bitrate) + int pixelCount = m_lx * m_ly; + int bitRate = pixelCount / 150; // crude but gets decent values + double quality = m_vidQuality / 100.0; + double tempRate = (double)bitRate * quality; + int finalBitrate = (int)tempRate; + int crf = 51 - (m_vidQuality * 51 / 100); + + preIArgs << "-framerate"; + preIArgs << QString::number(m_frameRate); + + postIArgs << "-c:v"; + postIArgs << "libvpx"; + postIArgs << "-s"; + postIArgs << QString::number(outLx) + "x" + QString::number(outLy); + postIArgs << "-b"; + postIArgs << QString::number(finalBitrate) + "k"; + postIArgs << "-speed"; + postIArgs << "3"; + postIArgs << "-quality"; + postIArgs << "good"; + + ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true); + ffmpegWriter->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriterWebm::getFrameWriter(TFrameId fid) { + // if (IOError != 0) + // throw TImageException(m_path, buildGifExceptionString(IOError)); + if (fid.getLetter() != 0) return TImageWriterP(0); + int index = fid.getNumber(); + TImageWriterWebm *iwg = new TImageWriterWebm(m_path, index, this); + return TImageWriterP(iwg); +} + +//----------------------------------------------------------- +void TLevelWriterWebm::setFrameRate(double fps) { + m_frameRate = fps; + ffmpegWriter->setFrameRate(fps); +} + +void TLevelWriterWebm::saveSoundTrack(TSoundTrack *st) { + ffmpegWriter->saveSoundTrack(st); +} + +//----------------------------------------------------------- + +void TLevelWriterWebm::save(const TImageP &img, int frameIndex) { + TRasterImageP image(img); + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + ffmpegWriter->createIntermediateImage(img, frameIndex); +} + +//=========================================================== +// +// TImageReaderWebm +// +//=========================================================== + +class TImageReaderWebm final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderWebm(const TFilePath &path, int index, TLevelReaderWebm *lra) + : TImageReader(path), m_lra(lra), m_frameIndex(index) { + m_lra->addRef(); + } + ~TImageReaderWebm() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + +private: + TLevelReaderWebm *m_lra; + + // not implemented + TImageReaderWebm(const TImageReaderWebm &); + TImageReaderWebm &operator=(const TImageReaderWebm &src); +}; + +//=========================================================== +// +// TLevelReaderWebm +// +//=========================================================== + +TLevelReaderWebm::TLevelReaderWebm(const TFilePath &path) : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + ffmpegReader->getFramesFromMovie(); + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; +} +//----------------------------------------------------------- + +TLevelReaderWebm::~TLevelReaderWebm() { + // ffmpegReader->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TLevelP TLevelReaderWebm::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderWebm::getFrameReader(TFrameId fid) { + // if (IOError != 0) + // throw TImageException(m_path, buildAVIExceptionString(IOError)); + if (fid.getLetter() != 0) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderWebm *irm = new TImageReaderWebm(m_path, index, this); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderWebm::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderWebm::load(int frameIndex) { + return ffmpegReader->getImage(frameIndex); +} + +Tiio::WebmWriterProperties::WebmWriterProperties() + : m_vidQuality("Quality", 1, 100, 90), m_scale("Scale", 1, 100, 100) { + bind(m_vidQuality); + bind(m_scale); +} + +// Tiio::Reader* Tiio::makeWebmReader(){ return nullptr; } +// Tiio::Writer* Tiio::makeWebmWriter(){ return nullptr; } \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_webm.h b/toonz/sources/image/ffmpeg/tiio_webm.h new file mode 100644 index 0000000..332db6e --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_webm.h @@ -0,0 +1,88 @@ +#pragma once + +#ifndef TTIO_WEBM_INCLUDED +#define TTIO_WEBM_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "tiio_ffmpeg.h" + +//=========================================================== +// +// TLevelWriterWebm +// +//=========================================================== + +class TLevelWriterWebm : public TLevelWriter { +public: + TLevelWriterWebm(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterWebm(); + void setFrameRate(double fps); + + TImageWriterP getFrameWriter(TFrameId fid) override; + void save(const TImageP &image, int frameIndex); + + void saveSoundTrack(TSoundTrack *st); + + static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { + return new TLevelWriterWebm(path, winfo); + } + +private: + Ffmpeg *ffmpegWriter; + int m_lx, m_ly; + int m_scale; + int m_vidQuality; + // void *m_buffer; +}; + +//=========================================================== +// +// TLevelReaderWebm +// +//=========================================================== + +class TLevelReaderWebm final : public TLevelReader { +public: + TLevelReaderWebm(const TFilePath &path); + ~TLevelReaderWebm(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderWebm(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); + // TThread::Mutex m_mutex; + // void *m_decompressedBuffer; +private: + Ffmpeg *ffmpegReader; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + +namespace Tiio { + +//=========================================================================== + +class WebmWriterProperties : public TPropertyGroup { +public: + // TEnumProperty m_pixelSize; + // TBoolProperty m_matte; + TIntProperty m_vidQuality; + TIntProperty m_scale; + WebmWriterProperties(); +}; + +//=========================================================================== + +// Tiio::Reader *makeWebmReader(); +// Tiio::Writer *makeWebmWriter(); + +} // namespace + +#endif diff --git a/toonz/sources/image/tiio.cpp b/toonz/sources/image/tiio.cpp index 6bb963c..b6a888b 100644 --- a/toonz/sources/image/tiio.cpp +++ b/toonz/sources/image/tiio.cpp @@ -72,6 +72,9 @@ #include "./pli/tiio_pli.h" #include "./tzl/tiio_tzl.h" #include "./svg/tiio_svg.h" +#include "./ffmpeg/tiio_gif.h" +#include "./ffmpeg/tiio_webm.h" +#include "./ffmpeg/tiio_mp4.h" #include "./mesh/tiio_mesh.h" //------------------------------------------------------------------- @@ -159,6 +162,35 @@ void initImageIo(bool lightVersion) { TFileType::declare("rgb", TFileType::RASTER_IMAGE); Tiio::defineWriterProperties("rgb", new Tiio::SgiWriterProperties()); + // ffmpeg +#if !defined(_WIN32) || defined(x64) + if (Ffmpeg::checkFfmpeg()) { + bool ffprobe = Ffmpeg::checkFfprobe(); + if (Ffmpeg::checkFormat("webm")) { + TLevelWriter::define("webm", TLevelWriterWebm::create, true); + if (ffprobe) + TLevelReader::define("webm", TLevelReaderWebm::create); + TFileType::declare("webm", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("webm", new Tiio::WebmWriterProperties()); + } + if (Ffmpeg::checkFormat("gif")) { + TLevelWriter::define("gif", TLevelWriterGif::create, true); + if (ffprobe) + TLevelReader::define("gif", TLevelReaderGif::create); + TFileType::declare("gif", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("gif", new Tiio::GifWriterProperties()); + } + if (Ffmpeg::checkFormat("mp4")) { + TLevelWriter::define("mp4", TLevelWriterMp4::create, true); + if (ffprobe) + TLevelReader::define("mp4", TLevelReaderMp4::create); + TFileType::declare("mp4", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("mp4", new Tiio::Mp4WriterProperties()); + } + } +#endif + // end ffmpeg + if (!lightVersion) { #ifdef _WIN32 diff --git a/toonz/sources/include/tlevel_io.h b/toonz/sources/include/tlevel_io.h index 4713788..63e274e 100644 --- a/toonz/sources/include/tlevel_io.h +++ b/toonz/sources/include/tlevel_io.h @@ -226,7 +226,8 @@ public: // Some useful utility inlines inline bool isMovieType(std::string type) { - return (type == "mov" || type == "avi" || type == "3gp"); + return (type == "mov" || type == "avi" || type == "3gp" || type == "webm" || + type == "mp4"); } //----------------------------------------------------------- diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index 188526a..f1b39fd 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -380,6 +380,14 @@ public: bool isAutomaticSVNFolderRefreshEnabled() const { return m_automaticSVNFolderRefreshEnabled; } + // Import Export Tab + + void setFfmpegPath(std::string path); + QString getFfmpegPath() const { return m_ffmpegPath; } + void setPrecompute(bool enabled); + bool getPrecompute() { return m_precompute; } + void setFfmpegTimeout(int seconds); + int getFfmpegTimeout() { return m_ffmpegTimeout; } // Uncategorized - internals @@ -419,7 +427,8 @@ private: std::vector m_levelFormats; QString m_units, m_cameraUnits, m_scanLevelType, m_currentRoomChoice, - m_oldUnits, m_oldCameraUnits; + m_oldUnits, m_oldCameraUnits, m_ffmpegPath; + ; double m_defLevelWidth, m_defLevelHeight, m_defLevelDpi; @@ -430,7 +439,8 @@ private: int m_autosavePeriod, // minutes m_chunkSize, m_blanksCount, m_onionPaperThickness, m_step, m_shrink, - m_textureSize, m_autocreationType, m_keyframeType, m_animationStep; + m_textureSize, m_autocreationType, m_keyframeType, m_animationStep, + m_ffmpegTimeout; //seconds int m_currentLanguage, m_currentStyleSheet, m_undoMemorySize, // in megabytes @@ -471,6 +481,7 @@ private: bool m_moveCurrentFrameByClickCellArea; bool m_onionSkinEnabled; bool m_multiLayerStylePickerEnabled; + bool m_precompute; /*-- Color Modelにラスタ画像を読み込んだとき、パレットをどのように作るか 0 : 全ての異なるピクセルの色を別のStyleにする, 1 : diff --git a/toonz/sources/toonz/formatsettingspopups.cpp b/toonz/sources/toonz/formatsettingspopups.cpp index e36e5e1..c053324 100644 --- a/toonz/sources/toonz/formatsettingspopups.cpp +++ b/toonz/sources/toonz/formatsettingspopups.cpp @@ -157,9 +157,11 @@ void FormatSettingsPopup::buildValueField(int index, TPropertyGroup *props) { m_mainLayout->addWidget(new QLabel(tr(prop->getName().c_str()) + ":", this), row, 0, Qt::AlignRight | Qt::AlignVCenter); m_mainLayout->addWidget(v, row, 1); - + // get value here - bug loses value if the range doesn't start with 0 + double value = prop->getValue(); v->setValues(prop->getValue(), prop->getRange().first, prop->getRange().second); + if (prop->getValue() != value) { prop->setValue(value); } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/iocommand.cpp b/toonz/sources/toonz/iocommand.cpp index 47e83c3..d9f6345 100644 --- a/toonz/sources/toonz/iocommand.cpp +++ b/toonz/sources/toonz/iocommand.cpp @@ -1287,12 +1287,11 @@ void IoCmd::newScene() { TDimensionD((double)res.lx / cameraDpi, (double)res.ly / cameraDpi)); scene->getProperties()->setBgColor(TPixel32::White); TProjectManager::instance()->initializeScene(scene); - if (Preferences::instance()->getPixelsOnly()) - { - TCamera *updateCamera = scene->getCurrentCamera(); - TDimension updateRes = updateCamera->getRes(); - updateCamera->setSize( - TDimensionD((double)updateRes.lx / cameraDpi, (double)updateRes.ly / cameraDpi)); + if (Preferences::instance()->getPixelsOnly()) { + TCamera *updateCamera = scene->getCurrentCamera(); + TDimension updateRes = updateCamera->getRes(); + updateCamera->setSize(TDimensionD((double)updateRes.lx / cameraDpi, + (double)updateRes.ly / cameraDpi)); } // Must set current scene after initializeScene!! app->getCurrentScene()->setScene(scene); @@ -1960,7 +1959,8 @@ std::vector //! Returns the number of actually loaded levels static int createSubXSheetFromPSDFolder(IoCmd::LoadResourceArguments &args, - TXsheet *xsh, int &col0, int psdLevelIndex, + TXsheet *xsh, int &col0, + int psdLevelIndex, PsdSettingsPopup *popup) { assert(popup->isFolder(psdLevelIndex)); @@ -2016,8 +2016,8 @@ static int createSubXSheetFromPSDFolder(IoCmd::LoadResourceArguments &args, // Load a psd file //! Returns the number of actually loaded levels -static int loadPSDResource(IoCmd::LoadResourceArguments &args, bool updateRecentFile, - PsdSettingsPopup *popup) { +static int loadPSDResource(IoCmd::LoadResourceArguments &args, + bool updateRecentFile, PsdSettingsPopup *popup) { int &row0 = args.row0; int &col0 = args.col0; int &row1 = args.row1; diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index a6ef674..eeea127 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -435,11 +435,23 @@ centralWidget->setLayout(centralWidgetLayout);*/ setCommandHandler(MI_About, this, &MainWindow::onAbout); setCommandHandler(MI_MaximizePanel, this, &MainWindow::maximizePanel); setCommandHandler(MI_FullScreenWindow, this, &MainWindow::fullScreenWindow); + //remove ffmpegCache if still exists from crashed exit + QString ffmpegCachePath = ToonzFolder::getCacheRootFolder().getQString() + "//ffmpeg"; + if (TSystem::doesExistFileOrLevel(TFilePath(ffmpegCachePath))) { + TSystem::rmDirTree(TFilePath(ffmpegCachePath)); + } } //----------------------------------------------------------------------------- -MainWindow::~MainWindow() { TEnv::saveAllEnvVariables(); } +MainWindow::~MainWindow() { + TEnv::saveAllEnvVariables(); + //cleanup ffmpeg cache + QString ffmpegCachePath = ToonzFolder::getCacheRootFolder().getQString() + "//ffmpeg"; + if (TSystem::doesExistFileOrLevel(TFilePath(ffmpegCachePath))) { + TSystem::rmDirTree(TFilePath(ffmpegCachePath)); + } +} //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/outputsettingspopup.cpp b/toonz/sources/toonz/outputsettingspopup.cpp index 78d58fa..e8a57b9 100644 --- a/toonz/sources/toonz/outputsettingspopup.cpp +++ b/toonz/sources/toonz/outputsettingspopup.cpp @@ -943,6 +943,13 @@ void OutputSettingsPopup::onFormatChanged(const QString &str) { TApp::instance()->getCurrentScene()->setDirtyFlag(true); if (m_presetCombo) m_presetCombo->setCurrentIndex(0); + if (str == "mp4" || str == "gif" || str == "webm") { + m_threadsComboOm->setDisabled(true); + m_threadsComboOm->setCurrentIndex(0); + } else { + m_threadsComboOm->setDisabled(false); + m_threadsComboOm->setCurrentIndex(2); + } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index 9c8cc9f..0ac1767 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -227,12 +227,15 @@ void PreferencesPopup::onPixelsOnlyChanged(int index) { camSize.lx = camRes.lx / 53.33333; camSize.ly = camRes.ly / 53.33333; camera->setSize(camSize); - TDimension cleanupRes = CleanupSettingsModel::instance()->getCurrentParameters()->m_camera.getRes(); + TDimension cleanupRes = CleanupSettingsModel::instance() + ->getCurrentParameters() + ->m_camera.getRes(); TDimensionD cleanupSize; cleanupSize.lx = cleanupRes.lx / 53.33333; cleanupSize.ly = cleanupRes.ly / 53.33333; - CleanupSettingsModel::instance()->getCurrentParameters()->m_camera.setSize(cleanupSize); - m_pref->storeOldUnits(); + CleanupSettingsModel::instance()->getCurrentParameters()->m_camera.setSize( + cleanupSize); + m_pref->storeOldUnits(); if (m_unitOm->currentIndex() != 4) m_unitOm->setCurrentIndex(4); if (m_cameraUnitOm->currentIndex() != 4) m_cameraUnitOm->setCurrentIndex(4); m_unitOm->setDisabled(true); @@ -839,6 +842,19 @@ void PreferencesPopup::onRegionAntialiasChanged(int on) { m_pref->setRegionAntialias(on); } +//----------------------------------------------------------------------------- + +void PreferencesPopup::onFfmpegPathChanged() { + QString text = m_ffmpegPathFileFld->getPath(); + m_pref->setFfmpegPath(text.toStdString()); +} + +//----------------------------------------------------------------------------- + +void PreferencesPopup::onFfmpegTimeoutChanged() { + m_pref->setFfmpegTimeout(m_ffmpegTimeout->getValue()); +} + //********************************************************************************** // PrefencesPopup's constructor //********************************************************************************** @@ -971,6 +987,11 @@ PreferencesPopup::PreferencesPopup() QComboBox *paletteTypeForRasterColorModelComboBox = new QComboBox(this); + //--- Import/Export ------------------------------ + categoryList->addItem(tr("Import/Export")); + m_ffmpegPathFileFld = new DVGui::FileField(this, QString("")); + m_ffmpegTimeout = new DVGui::IntLineEdit(this, 30, 1); + //--- Drawing ------------------------------ categoryList->addItem(tr("Drawing")); @@ -1170,6 +1191,11 @@ PreferencesPopup::PreferencesPopup() paletteTypeForRasterColorModelComboBox->setCurrentIndex( m_pref->getPaletteTypeOnLoadRasterImageAsColorModel()); + //--- Import/Export ------------------------------ + QString path = m_pref->getFfmpegPath(); + m_ffmpegPathFileFld->setPath(path); + m_ffmpegTimeout->setValue(m_pref->getFfmpegTimeout()); + //--- Drawing ------------------------------ keepOriginalCleanedUpCB->setChecked(m_pref->isSaveUnpaintedInCleanupEnable()); multiLayerStylePickerCB->setChecked(m_pref->isMultiLayerStylePickerEnabled()); @@ -1505,6 +1531,55 @@ PreferencesPopup::PreferencesPopup() loadingBox->setLayout(loadingFrameLay); stackedWidget->addWidget(loadingBox); + //--- Import/Export -------------------------- + QWidget *ioBox = new QWidget(this); + QVBoxLayout *ioLay = new QVBoxLayout(); + ioLay->setMargin(15); + ioLay->setSpacing(10); + { + ioLay->addWidget( + new QLabel( + tr("OpenToonz can use FFmpeg for additional file formats.")), + 0, Qt::AlignCenter | Qt::AlignVCenter); + ioLay->addWidget(new QLabel(tr("FFmpeg is not bundled with OpenToonz")), + 0, Qt::AlignCenter | Qt::AlignVCenter); + ioLay->addWidget(new QLabel(" "), + 0, Qt::AlignCenter | Qt::AlignVCenter); + ioLay->addWidget(new QLabel(tr("NOTE: This is an experimental feature.")), + 0, Qt::AlignCenter | Qt::AlignVCenter); + ioLay->addWidget(new QLabel(tr("Please SAVE YOUR WORK before exporting " + "in MP4, WEBM, or GIF format.")), + 0, Qt::AlignCenter | Qt::AlignVCenter); + ioLay->addWidget(new QLabel(" "), + 0, Qt::AlignCenter | Qt::AlignVCenter); + ioLay->addWidget(new QLabel(tr("Please provide the path where FFmpeg is " + "located on your computer.")), + 0, Qt::AlignLeft | Qt::AlignVCenter); + QGridLayout *ioGridLay = new QGridLayout(); + ioGridLay->setVerticalSpacing(10); + ioGridLay->setHorizontalSpacing(15); + ioGridLay->setMargin(0); + { + ioGridLay->addWidget(new QLabel(tr("FFmpeg Path: ")), 0, 0, + Qt::AlignRight); + ioGridLay->addWidget(m_ffmpegPathFileFld, 0, 1, 1, 3); + ioGridLay->addWidget(new QLabel(" "), 1, 0); + ioGridLay->addWidget(new QLabel(tr("Number of seconds to wait for FFmpeg to complete " + "processing the output:")), 2, 0, 1, 4); + ioGridLay->addWidget(new QLabel(tr("Note: FFmpeg begins working once all images " + "have been processed.")), 3, 0, 1, 4); + ioGridLay->addWidget(new QLabel(tr("FFmpeg Timeout:")), 4, 0, + Qt::AlignRight); + ioGridLay->addWidget(m_ffmpegTimeout, 4, 1, 1, 3); + } + ioLay->addLayout(ioGridLay); + ioLay->addStretch(1); + + ioLay->addWidget(note_version, 0); + } + ioBox->setLayout(ioLay); + stackedWidget->addWidget(ioBox); + //--- Drawing -------------------------- QWidget *drawingBox = new QWidget(this); QVBoxLayout *drawingFrameLay = new QVBoxLayout(); @@ -1841,6 +1916,12 @@ PreferencesPopup::PreferencesPopup() SIGNAL(currentIndexChanged(int)), this, SLOT(onPaletteTypeForRasterColorModelChanged(int))); + //--- Import/Export ---------------------- + ret = ret && connect(m_ffmpegPathFileFld, SIGNAL(pathChanged()), this, + SLOT(onFfmpegPathChanged())); + ret = ret && connect(m_ffmpegTimeout, SIGNAL(editingFinished()), this, + SLOT(onFfmpegTimeoutChanged())); + //--- Drawing ---------------------- ret = ret && connect(keepOriginalCleanedUpCB, SIGNAL(stateChanged(int)), this, SLOT(onSaveUnpaintedInCleanupChanged(int))); diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index 07f0c9d..8e8b640 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -9,6 +9,7 @@ #include "toonzqt/doublefield.h" #include "toonzqt/colorfield.h" #include "toonzqt/checkbox.h" +#include "toonzqt/filefield.h" // TnzLib includes #include "toonz/preferences.h" @@ -62,13 +63,15 @@ private: DVGui::IntLineEdit *m_minuteFld, *m_chunkSizeFld, *m_iconSizeLx, *m_iconSizeLy, *m_viewShrink, *m_viewStep, *m_blanksCount, *m_onionPaperThickness, *m_animationStepField, *m_undoMemorySize, - *m_xsheetStep; + *m_xsheetStep, *m_ffmpegTimeout; QPushButton *m_addLevelFormat, *m_removeLevelFormat, *m_editLevelFormat; DVGui::CheckBox *m_inksOnly, *m_enableVersionControl, *m_levelsBackup, *m_onionSkinVisibility, *m_pixelsOnlyCB; + DVGui::FileField *m_ffmpegPathFileFld; + private: // QWidget* create(const QString& lbl, bool def, const char* slot); void rebuildFormatsList(); @@ -148,6 +151,8 @@ private slots: void onShowFrameNumberWithLettersChanged(int index); void onPaletteTypeForRasterColorModelChanged(int index); void onShowKeyframesOnCellAreaChanged(int); + void onFfmpegPathChanged(); + void onFfmpegTimeoutChanged(); }; //********************************************************************************** diff --git a/toonz/sources/toonz/rendercommand.cpp b/toonz/sources/toonz/rendercommand.cpp index 1171ddb..adaf9f8 100644 --- a/toonz/sources/toonz/rendercommand.cpp +++ b/toonz/sources/toonz/rendercommand.cpp @@ -128,7 +128,8 @@ public: if (Preferences::instance()->isGeneratedMovieViewEnabled()) { if (!isPreview && (Preferences::instance()->isDefaultViewerEnabled()) && (m_fp.getType() == "mov" || m_fp.getType() == "avi" || - m_fp.getType() == "3gp")) { + m_fp.getType() == "3gp" || m_fp.getType() == "mp4" || + m_fp.getType() == "gif" || m_fp.getType() == "webm")) { QString name = QString::fromStdString(m_fp.getName()); int index; if ((index = name.indexOf("#RENDERID")) != -1) //! quite ugly I @@ -169,15 +170,19 @@ public: prop->m_frameRate = outputSettings.getFrameRate(); TSoundTrack *snd = app->getCurrentXsheet()->getXsheet()->makeSound(prop); - if (outputSettings.getRenderSettings().m_stereoscopic) { - assert(!isPreview); - ::viewFile(m_fp.withName(m_fp.getName() + "_l"), r0 + 1, r1 + 1, step, - isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); - ::viewFile(m_fp.withName(m_fp.getName() + "_r"), r0 + 1, r1 + 1, step, - isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); - } else - ::viewFile(m_fp, r0 + 1, r1 + 1, step, isPreview ? rs.m_shrinkX : 1, - snd, 0, false, true); + // keeps ffmpeg files from being previewed until import is fixed + if (m_fp.getType() != "mp4" && m_fp.getType() != "webm" && + m_fp.getType() != "gif") { + if (outputSettings.getRenderSettings().m_stereoscopic) { + assert(!isPreview); + ::viewFile(m_fp.withName(m_fp.getName() + "_l"), r0 + 1, r1 + 1, + step, isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); + ::viewFile(m_fp.withName(m_fp.getName() + "_r"), r0 + 1, r1 + 1, + step, isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); + } else + ::viewFile(m_fp, r0 + 1, r1 + 1, step, isPreview ? rs.m_shrinkX : 1, + snd, 0, false, true); + } } } } @@ -381,7 +386,7 @@ void RenderCommand::flashRender() { class RenderListener final : public DVGui::ProgressDialog, public MovieRenderer::Listener { QString m_progressBarString; - int m_frameCounter; + int m_frameCounter, m_totalFrames; TRenderer *m_renderer; bool m_error; @@ -428,13 +433,17 @@ public: m_progressBarString = QString::number(steps) + ((isPreview) ? "" : " of " + toQString(path)); // setMinimumDuration (0); + m_totalFrames = steps; show(); } /*-- 以下3つの関数はMovieRenderer::Listenerの純粋仮想関数の実装 --*/ bool onFrameCompleted(int frame) override { bool ret = wasCanceled(); - Message(this, ret ? -1 : ++m_frameCounter, m_progressBarString).send(); + if (m_frameCounter + 1 < m_totalFrames) + Message(this, ret ? -1 : ++m_frameCounter, m_progressBarString).send(); + else + setLabelText(tr("Finalizing render, please wait.")); return !ret; } bool onFrameFailed(int frame, TException &) override { @@ -491,7 +500,10 @@ void RenderCommand::rasterRender(bool isPreview) { TPixel32 currBgColor = scene->getProperties()->getBgColor(); m_priorBgColor = currBgColor; - if (ext == "jpg" || ext == "avi" || ext == "bmp") { + // fixes background colors for non alpha-enabled export types (eventually + // transparent gif would be good) + if (ext == "jpg" || ext == "avi" || ext == "bmp" || ext == "mp4" || + ext == "webm" || ext == "gif") { currBgColor.m = 255; scene->getProperties()->setBgColor(currBgColor); } @@ -532,7 +544,11 @@ void RenderCommand::rasterRender(bool isPreview) { TPointD cameraDpi = isPreview ? scene->getCurrentPreviewCamera()->getDpi() : scene->getCurrentCamera()->getDpi(); movieRenderer.setDpi(cameraDpi.x, cameraDpi.y); - movieRenderer.enablePrecomputing(true); + // Precomputing causes a long delay for ffmpeg imported types + if (!isPreview && !Preferences::instance()->getPrecompute()) + movieRenderer.enablePrecomputing(false); + else + movieRenderer.enablePrecomputing(true); /*-- プログレス ダイアログの作成 --*/ RenderListener *listener = diff --git a/toonz/sources/toonz/tasksviewer.cpp b/toonz/sources/toonz/tasksviewer.cpp index b9e99d8..1b2a985 100644 --- a/toonz/sources/toonz/tasksviewer.cpp +++ b/toonz/sources/toonz/tasksviewer.cpp @@ -36,7 +36,8 @@ using namespace DVGui; namespace { bool isMovieType(std::string type) { - return (type == "mov" || type == "avi" || type == "3gp"); + return (type == "mov" || type == "avi" || type == "3gp" || type == "mp4" || + type == "webm"); } }; diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index bc6c8b6..0c151a3 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -285,7 +285,9 @@ Preferences::Preferences() , m_onionSkinEnabled(true) , m_multiLayerStylePickerEnabled(false) , m_paletteTypeOnLoadRasterImageAsColorModel(0) - , m_showKeyframesOnXsheetCellArea(true) { + , m_showKeyframesOnXsheetCellArea(true) + , m_precompute(true) + , m_ffmpegTimeout(30) { TCamera camera; m_defLevelType = PLI_XSHLEVEL; m_defLevelWidth = camera.getSize().lx; @@ -539,6 +541,11 @@ Preferences::Preferences() m_paletteTypeOnLoadRasterImageAsColorModel); getValue(*m_settings, "showKeyframesOnXsheetCellArea", m_showKeyframesOnXsheetCellArea); + QString ffmpegPath = m_settings->value("ffmpegPath").toString(); + if (ffmpegPath != "") m_ffmpegPath = ffmpegPath; + setFfmpegPath(m_ffmpegPath.toStdString()); + getValue(*m_settings, "ffmpegTimeout", + m_ffmpegTimeout); } //----------------------------------------------------------------- @@ -1179,6 +1186,26 @@ void Preferences::setPaletteTypeOnLoadRasterImageAsColorModel(int type) { //----------------------------------------------------------------- +void Preferences::setFfmpegPath(std::string path) { + m_ffmpegPath = QString::fromStdString(path); + std::string strPath = m_ffmpegPath.toStdString(); + m_settings->setValue("ffmpegPath", m_ffmpegPath); +} + +//----------------------------------------------------------------- + +void Preferences::setPrecompute(bool enabled) { m_precompute = enabled; } + +//----------------------------------------------------------------- + +void Preferences::setFfmpegTimeout(int seconds) { + m_ffmpegTimeout = seconds; + m_settings->setValue("ffmpegTimeout", seconds); +} + +//----------------------------------------------------------------- + + int Preferences::addLevelFormat(const LevelFormat &format) { LevelFormatVector::iterator lft = m_levelFormats.insert( std::upper_bound(m_levelFormats.begin(), m_levelFormats.end(), format, diff --git a/toonz/sources/toonzlib/tcolumnfx.cpp b/toonz/sources/toonzlib/tcolumnfx.cpp index e9a7452..b85566a 100644 --- a/toonz/sources/toonzlib/tcolumnfx.cpp +++ b/toonz/sources/toonzlib/tcolumnfx.cpp @@ -1348,15 +1348,23 @@ void TLevelColumnFx::getImageInfo(TImageInfo &info, TXshSimpleLevel *sl, if (!storedInfo) // sulle pict caricate info era nullo, ma l'immagine c'e'! // con la getFullSampleFrame riprendo l'immagine e ricalcolo la savebox... { - TImageP img; + TImageP img; if (!(img = sl->getFullsampledFrame(frameId, ImageManager::dontPutInCache))) { assert(false); return; } - - info.m_lx = (int)img->getBBox().getLx(); - info.m_ly = (int)img->getBBox().getLy(); + //Raster levels from ffmpeg were not giving the right dimensions without the raster cast and check + TRasterImageP rasterImage = (TRasterImageP)img; + if (rasterImage) + { + info.m_lx = (int)rasterImage->getRaster()->getLx(); + info.m_ly = (int)rasterImage->getRaster()->getLy(); + } + else { + info.m_lx = (int)img->getBBox().getLx(); + info.m_ly = (int)img->getBBox().getLy(); + } info.m_x0 = info.m_y0 = 0; info.m_x1 = (int)img->getBBox().getP11().x; info.m_y1 = (int)img->getBBox().getP11().y; diff --git a/toonz/sources/toonzqt/dvdialog.cpp b/toonz/sources/toonzqt/dvdialog.cpp index c36ee18..3692f7e 100644 --- a/toonz/sources/toonzqt/dvdialog.cpp +++ b/toonz/sources/toonzqt/dvdialog.cpp @@ -805,7 +805,7 @@ ProgressDialog::ProgressDialog(const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent, Qt::WindowFlags f) : Dialog(parent, true, true), m_isCanceled(false) { - setWindowTitle(tr("Toonz")); + setWindowTitle(tr("OpenToonz")); setMinimumSize(20, 20);