diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp index 6f958ceb..1734a1b7 100644 --- a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp @@ -258,8 +258,8 @@ ffmpegFileInfo Ffmpeg::getInfo() { } else { QFile infoText(tempPath); getSize(); - getFrameRate(); getFrameCount(); + getFrameRate(); infoText.open(QIODevice::WriteOnly); std::string infoToWrite = std::to_string(m_lx) + " " + std::to_string(m_ly) + " " + @@ -306,24 +306,22 @@ TRasterImageP Ffmpeg::getImage(int frameIndex) { } 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); + QStringList fpsArgs; + fpsArgs << "-v"; + fpsArgs << "error"; + fpsArgs << "-select_streams"; + fpsArgs << "v:0"; + fpsArgs << "-show_entries"; + fpsArgs << "stream=r_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; - } + int fpsNum = fpsResults.split("/")[0].toInt(); + int fpsDen = fpsResults.split("/")[1].toInt(); + if (fpsDen > 0) { + m_frameRate = (double)fpsNum / (double)fpsDen; } return m_frameRate; } @@ -355,13 +353,13 @@ int Ffmpeg::getFrameCount() { frameCountArgs << "-select_streams"; frameCountArgs << "v:0"; frameCountArgs << "-show_entries"; - frameCountArgs << "stream=nb_read_frames"; + frameCountArgs << "stream=duration"; frameCountArgs << "-of"; frameCountArgs << "default=nokey=1:noprint_wrappers=1"; frameCountArgs << m_path.getQString(); QString frameResults = runFfprobe(frameCountArgs); - m_frameCount = frameResults.toInt(); + m_frameCount = frameResults.toDouble() * getFrameRate(); return m_frameCount; } diff --git a/toonz/sources/image/ffmpeg/tiio_gif.cpp b/toonz/sources/image/ffmpeg/tiio_gif.cpp index d834ee38..7a6f02f9 100644 --- a/toonz/sources/image/ffmpeg/tiio_gif.cpp +++ b/toonz/sources/image/ffmpeg/tiio_gif.cpp @@ -92,7 +92,7 @@ TLevelWriterGif::~TLevelWriterGif() { preIArgs << "-v"; preIArgs << "warning"; preIArgs << "-r"; - preIArgs << QString::number(m_frameRate); + preIArgs << QString::number((m_frameRate < 1 ? 12.0 : m_frameRate)); if (m_palette) { postIArgs << "-i"; postIArgs << palette; @@ -194,8 +194,6 @@ TLevelReaderGif::TLevelReaderGif(const TFilePath &path) m_lx = m_size.lx; m_ly = m_size.ly; - ffmpegReader->getFramesFromMovie(); - m_frameCount = ffmpegReader->getGifFrameCount(); // set values m_info = new TImageInfo(); m_info->m_frameRate = fps; @@ -239,6 +237,10 @@ TDimension TLevelReaderGif::getSize() { return m_size; } //------------------------------------------------ TImageP TLevelReaderGif::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } return ffmpegReader->getImage(frameIndex); } diff --git a/toonz/sources/image/ffmpeg/tiio_gif.h b/toonz/sources/image/ffmpeg/tiio_gif.h index 2f6fa7e4..fd9bff98 100644 --- a/toonz/sources/image/ffmpeg/tiio_gif.h +++ b/toonz/sources/image/ffmpeg/tiio_gif.h @@ -64,6 +64,7 @@ public: // void *m_decompressedBuffer; private: Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; TDimension m_size; int m_frameCount, m_lx, m_ly; }; diff --git a/toonz/sources/image/ffmpeg/tiio_mp4.cpp b/toonz/sources/image/ffmpeg/tiio_mp4.cpp index a0529d85..4c1b1768 100644 --- a/toonz/sources/image/ffmpeg/tiio_mp4.cpp +++ b/toonz/sources/image/ffmpeg/tiio_mp4.cpp @@ -173,8 +173,6 @@ TLevelReaderMp4::TLevelReaderMp4(const TFilePath &path) : TLevelReader(path) { m_lx = m_size.lx; m_ly = m_size.ly; - ffmpegReader->getFramesFromMovie(); - // set values m_info = new TImageInfo(); m_info->m_frameRate = fps; @@ -219,6 +217,10 @@ TDimension TLevelReaderMp4::getSize() { return m_size; } //------------------------------------------------ TImageP TLevelReaderMp4::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } return ffmpegReader->getImage(frameIndex); } diff --git a/toonz/sources/image/ffmpeg/tiio_mp4.h b/toonz/sources/image/ffmpeg/tiio_mp4.h index 2b5d6d8b..87480370 100644 --- a/toonz/sources/image/ffmpeg/tiio_mp4.h +++ b/toonz/sources/image/ffmpeg/tiio_mp4.h @@ -61,6 +61,7 @@ public: // void *m_decompressedBuffer; private: Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; TDimension m_size; int m_frameCount, m_lx, m_ly; }; diff --git a/toonz/sources/image/ffmpeg/tiio_webm.cpp b/toonz/sources/image/ffmpeg/tiio_webm.cpp index c4968419..e54c7adc 100644 --- a/toonz/sources/image/ffmpeg/tiio_webm.cpp +++ b/toonz/sources/image/ffmpeg/tiio_webm.cpp @@ -174,8 +174,6 @@ TLevelReaderWebm::TLevelReaderWebm(const TFilePath &path) : TLevelReader(path) { m_lx = m_size.lx; m_ly = m_size.ly; - ffmpegReader->getFramesFromMovie(); - // set values m_info = new TImageInfo(); m_info->m_frameRate = fps; @@ -220,6 +218,10 @@ TDimension TLevelReaderWebm::getSize() { return m_size; } //------------------------------------------------ TImageP TLevelReaderWebm::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } return ffmpegReader->getImage(frameIndex); } diff --git a/toonz/sources/image/ffmpeg/tiio_webm.h b/toonz/sources/image/ffmpeg/tiio_webm.h index afaf8c60..01ff39dd 100644 --- a/toonz/sources/image/ffmpeg/tiio_webm.h +++ b/toonz/sources/image/ffmpeg/tiio_webm.h @@ -60,6 +60,7 @@ public: // void *m_decompressedBuffer; private: Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; TDimension m_size; int m_frameCount, m_lx, m_ly; }; diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 6a98bbaf..3f19d276 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -990,6 +990,9 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { // Check level type write support if (sl->getPath().getType() == "psd" || // We don't have the API to write psd files + sl->getPath().getType() == "gif" || + sl->getPath().getType() == "mp4" || + sl->getPath().getType() == "webm" || sl->is16BitChannelLevel() || // Inherited by previous implementation. // Could be fixed? sl->getProperties()->getBpp() == diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index c50f61ce..ec023a59 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -495,7 +495,9 @@ bool pasteStrokesInCellWithoutUndo( } else { vi = cell.getImage(true); sl = cell.getSimpleLevel(); - if (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() == "psd") + if (sl->getType() == OVL_XSHLEVEL && + (sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" || + sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm")) return false; fid = cell.getFrameId(); if (!vi) { @@ -1561,7 +1563,9 @@ static void pasteRasterImageInCell(int row, int col, if (sl) oldPalette = sl->getPalette(); } else { TXshSimpleLevel *sl = cell.getSimpleLevel(); - if (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() == "psd") + if (sl->getType() == OVL_XSHLEVEL && + (sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" || + sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm")) return; oldPalette = sl->getPalette(); } diff --git a/toonz/sources/toonz/cellselectioncommand.cpp b/toonz/sources/toonz/cellselectioncommand.cpp index f7b76d5a..918f4ed6 100644 --- a/toonz/sources/toonz/cellselectioncommand.cpp +++ b/toonz/sources/toonz/cellselectioncommand.cpp @@ -599,7 +599,7 @@ ReframeUndo::ReframeUndo(int r0, int r1, std::vector columnIndeces, assert(m_cells); int k = 0; for (int r = r0; r <= r1; r++) - for (int c = 0; c < (int)m_columnIndeces.size(); c++) + for (int c = 0; c < (int)m_columnIndeces.size(); c++) m_cells[k++] = TApp::instance()->getCurrentXsheet()->getXsheet()->getCell( r, m_columnIndeces[c]); @@ -719,7 +719,7 @@ void TCellSelection::reframeWithEmptyInbetweens() { // destruction of m_reframePopup will be done along with the main window if (!m_reframePopup) m_reframePopup = new ReframePopup(); - int ret = m_reframePopup->exec(); + int ret = m_reframePopup->exec(); if (ret == QDialog::Rejected) return; int step, blank; @@ -762,7 +762,7 @@ void TColumnSelection::reframeWithEmptyInbetweens() { colIndeces.push_back(*it); if (!m_reframePopup) m_reframePopup = new ReframePopup(); - int ret = m_reframePopup->exec(); + int ret = m_reframePopup->exec(); if (ret == QDialog::Rejected) return; int step, blank; @@ -1296,8 +1296,9 @@ public: void undo() const override; int getSize() const override { - return sizeof *this + (sizeof(TXshLevelP) + sizeof(TXshSimpleLevel *)) * - m_insertedLevels.size(); + return sizeof *this + + (sizeof(TXshLevelP) + sizeof(TXshSimpleLevel *)) * + m_insertedLevels.size(); } QString getHistoryString() override { @@ -1526,7 +1527,11 @@ void CloneLevelUndo::cloneLevels() const { assert(lt->first && !lt->second.empty()); TXshSimpleLevel *srcSl = lt->first; - if (srcSl->getPath().getType() == "psd") continue; + if (srcSl->getPath().getType() == "psd" || + srcSl->getPath().getType() == "gif" || + srcSl->getPath().getType() == "mp4" || + srcSl->getPath().getType() == "webm") + continue; const TFilePath &srcPath = srcSl->getPath(); diff --git a/toonz/sources/toonz/columnselection.cpp b/toonz/sources/toonz/columnselection.cpp index 95c3bea2..c58e3aea 100644 --- a/toonz/sources/toonz/columnselection.cpp +++ b/toonz/sources/toonz/columnselection.cpp @@ -179,7 +179,10 @@ static bool canMergeColumns(int column, int mColumn, bool forMatchlines) { return false; // Check level type write support. Based on TTool::updateEnabled() if (level->getType() == OVL_XSHLEVEL && - (level->getPath().getType() == "psd" || // PSD files. + (level->getPath().getType() == "psd" || // PSD files. + level->getPath().getType() == "gif" || + level->getPath().getType() == "mp4" || + level->getPath().getType() == "webm" || level->is16BitChannelLevel() || // 16bpc images. level->getProperties()->getBpp() == 1)) { // Black & White images. return false; diff --git a/toonz/sources/toonz/filmstrip.cpp b/toonz/sources/toonz/filmstrip.cpp index 7fe018d2..c2688102 100644 --- a/toonz/sources/toonz/filmstrip.cpp +++ b/toonz/sources/toonz/filmstrip.cpp @@ -1065,7 +1065,10 @@ void FilmstripFrames::contextMenuEvent(QContextMenuEvent *event) { menu->addAction(cm->getAction(MI_RevertToCleanedUp)); if (sl && (sl->getType() == TZP_XSHLEVEL || sl->getType() == PLI_XSHLEVEL || - (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "psd"))) + (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "psd" && + sl->getPath().getType() != "gif" && + sl->getPath().getType() != "mp4" && + sl->getPath().getType() != "webm"))) menu->addAction(cm->getAction(MI_RevertToLastSaved)); } diff --git a/toonz/sources/toonz/filmstripselection.cpp b/toonz/sources/toonz/filmstripselection.cpp index 7e30d221..663d98a9 100644 --- a/toonz/sources/toonz/filmstripselection.cpp +++ b/toonz/sources/toonz/filmstripselection.cpp @@ -57,7 +57,9 @@ void TFilmstripSelection::enableCommands() { bool doEnable = (type == PLI_XSHLEVEL || type == TZP_XSHLEVEL || type == MESH_XSHLEVEL || - (type == OVL_XSHLEVEL && path.getType() != "psd")); + (type == OVL_XSHLEVEL && path.getType() != "psd" && + path.getType() != "gif" && path.getType() != "mp4" && + path.getType() != "webm")); TRasterImageP ri = (TRasterImageP)sl->getSimpleLevel()->getFrame( sl->getSimpleLevel()->getFirstFid(), false); diff --git a/toonz/sources/toonz/matchlinecommand.cpp b/toonz/sources/toonz/matchlinecommand.cpp index c4df7602..59ed43a1 100644 --- a/toonz/sources/toonz/matchlinecommand.cpp +++ b/toonz/sources/toonz/matchlinecommand.cpp @@ -309,7 +309,10 @@ void doCloneLevelNoSave(const TCellSelection::Range &range, fid = cell.getFrameId(); if (cell.getSimpleLevel() == 0 || - cell.getSimpleLevel()->getPath().getType() == "psd") + cell.getSimpleLevel()->getPath().getType() == "psd" || + cell.getSimpleLevel()->getPath().getType() == "gif" || + cell.getSimpleLevel()->getPath().getType() == "mp4" || + cell.getSimpleLevel()->getPath().getType() == "webm") continue; std::map::iterator it = diff --git a/toonz/sources/toonzlib/txshsimplelevel.cpp b/toonz/sources/toonzlib/txshsimplelevel.cpp index 9fbf4176..c50d43c0 100644 --- a/toonz/sources/toonzlib/txshsimplelevel.cpp +++ b/toonz/sources/toonzlib/txshsimplelevel.cpp @@ -104,8 +104,8 @@ bool checkCreatorString(const QString &creator) { if (pos >= 0 && len >= 4) { QString v; if (len > 4) v = creator.mid(pos + 3, len - 4); - bool ok = true; - mask = v.toInt(&ok, 16); + bool ok = true; + mask = v.toInt(&ok, 16); } } return (mask & compatibility.neededMask) == compatibility.neededMask && @@ -119,7 +119,9 @@ bool isAreadOnlyLevel(const TFilePath &path) { if (path.getDots() == "." || (path.getDots() == ".." && (path.getType() == "tlv" || path.getType() == "tpl"))) { - if (path.getType() == "psd") return true; + if (path.getType() == "psd" || path.getType() == "gif" || + path.getType() == "mp4" || path.getType() == "webm") + return true; if (!TSystem::doesExistFileOrLevel(path)) return false; TFileStatus fs(path); return !fs.isWritable(); @@ -169,8 +171,8 @@ void getIndexesRangefromFids(TXshSimpleLevel *level, std::set::const_iterator it; for (it = fids.begin(); it != fids.end(); ++it) { - int index = level->guessIndex(*it); - if (index > toIndex) toIndex = index; + int index = level->guessIndex(*it); + if (index > toIndex) toIndex = index; if (index < fromIndex) fromIndex = index; } } @@ -909,13 +911,13 @@ void TXshSimpleLevel::loadData(TIStream &is) { if (is.getTagParam("dpix", v)) xdpi = std::stod(v); if (is.getTagParam("dpiy", v)) ydpi = std::stod(v); if (xdpi != 0 && ydpi != 0) dpiPolicy = LevelProperties::DP_CustomDpi; - std::string dpiType = is.getTagAttribute("dpiType"); - if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi; - if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL; - if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v); - if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v); + std::string dpiType = is.getTagAttribute("dpiType"); + if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi; + if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL; + if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v); + if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v); if (is.getTagParam("antialias", v)) antialiasSoftness = std::stoi(v); - if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v); + if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v); m_properties->setDpiPolicy(dpiPolicy); m_properties->setDpi(TPointD(xdpi, ydpi)); @@ -1046,8 +1048,9 @@ static TFilePath getLevelPathAndSetNameWithPsdLevelName( if (removeFileName) wLevelName = list[1].toStdWString(); TLevelSet *levelSet = xshLevel->getScene()->getLevelSet(); - if (levelSet && levelSet->hasLevel( - wLevelName)) // levelSet should be asserted instead + if (levelSet && + levelSet->hasLevel( + wLevelName)) // levelSet should be asserted instead levelSet->renameLevel(xshLevel, wLevelName); xshLevel->setName(wLevelName); @@ -1580,8 +1583,8 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp, std::vector fids; getFids(fids); - bool isLevelModified = getProperties()->getDirtyFlag(); - bool isPaletteModified = false; + bool isLevelModified = getProperties()->getDirtyFlag(); + bool isPaletteModified = false; if (getPalette()) isPaletteModified = getPalette()->getDirtyFlag(); if (isLevelModified || isPaletteModified) { @@ -1712,6 +1715,9 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp, // strategia LevelUpdater updater(this); updater.getLevelWriter()->setCreator(getCreatorString()); + if (updater.getImageInfo()) + updater.getLevelWriter()->setFrameRate( + updater.getImageInfo()->m_frameRate); if (isLevelModified) { // Apply the level's renumber table, before saving other files. @@ -1824,11 +1830,11 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp, std::string TXshSimpleLevel::getImageId(const TFrameId &fid, int frameStatus) const { if (frameStatus < 0) frameStatus = getFrameStatus(fid); - std::string prefix = "L"; + std::string prefix = "L"; if (frameStatus & CleanupPreview) prefix = "P"; else if ((frameStatus & (Scanned | Cleanupped)) == Scanned) - prefix = "S"; + prefix = "S"; std::string imageId = m_idBase + "_" + prefix + fid.expand(); return imageId; } @@ -2254,14 +2260,13 @@ TFilePath TXshSimpleLevel::getExistingHookFile( int f, fCount = hookFiles.size(); for (f = 0; f != fCount; ++f) { - fPattern = locals::getPattern(hookFiles[f]); + fPattern = locals::getPattern(hookFiles[f]); if (fPattern < p) p = fPattern, h = f; } assert(h >= 0); - return (h < 0) ? TFilePath() - : decodedLevelPath.getParentDir() + - TFilePath(hookFiles[h].toStdWString()); + return (h < 0) ? TFilePath() : decodedLevelPath.getParentDir() + + TFilePath(hookFiles[h].toStdWString()); } //----------------------------------------------------------------------------- @@ -2294,8 +2299,8 @@ TRectD TXshSimpleLevel::getBBox(const TFrameId &fid) const { if (!info) return TRectD(); bbox = TRectD(TPointD(info->m_x0, info->m_y0), - TPointD(info->m_x1, info->m_y1)) - - 0.5 * TPointD(info->m_lx, info->m_ly); + TPointD(info->m_x1, info->m_y1)) - + 0.5 * TPointD(info->m_lx, info->m_ly); if (info->m_dpix > 0.0 && info->m_dpiy > 0.0) dpiX = info->m_dpix, dpiY = info->m_dpiy;