From 2c5f6553638e2568a3a410185dbda2a6d887096f Mon Sep 17 00:00:00 2001 From: shun-iwasawa Date: Wed, 14 Dec 2022 09:55:24 +0900 Subject: [PATCH] enhance viewer preview - render all frames, multi-thread mode - render cell selection range mode - enable to set shortcut key - enable to set sub camera larger than the original --- toonz/sources/include/toonzqt/flipconsole.h | 3 + toonz/sources/toonz/mainwindow.cpp | 30 +++ toonz/sources/toonz/mainwindow.h | 1 + toonz/sources/toonz/menubarcommandids.h | 2 + toonz/sources/toonz/pane.cpp | 53 +++++ toonz/sources/toonz/pane.h | 25 ++- toonz/sources/toonz/previewer.cpp | 107 +++++++++-- toonz/sources/toonz/previewer.h | 1 + toonz/sources/toonz/sceneviewer.cpp | 65 +++++-- toonz/sources/toonz/subcameramanager.cpp | 64 ++++++- toonz/sources/toonz/subcameramanager.h | 2 +- toonz/sources/toonz/viewerpane.cpp | 202 +++++++++++++++++++- toonz/sources/toonz/viewerpane.h | 14 +- toonz/sources/toonzlib/tcamera.cpp | 25 +-- toonz/sources/toonzqt/flipconsole.cpp | 19 +- 15 files changed, 551 insertions(+), 62 deletions(-) diff --git a/toonz/sources/include/toonzqt/flipconsole.h b/toonz/sources/include/toonzqt/flipconsole.h index 04094e55..bee23ed8 100644 --- a/toonz/sources/include/toonzqt/flipconsole.h +++ b/toonz/sources/include/toonzqt/flipconsole.h @@ -300,6 +300,7 @@ public: UINT getCustomizeMask() { return m_customizeMask; } void setCustomizemask(UINT mask); void setStopAt(int frame); + void setStartAt(int frame); // the main (currently the only) use for current flipconsole and setActive is // to @@ -382,6 +383,8 @@ private: int m_from, m_to, m_step; int m_currentFrame, m_framesCount; int m_stopAt = -1; + int m_startAt = + -1; // used in the "play selection" mode of the viewer preview ImagePainter::VisualSettings m_settings; bool m_isPlay; diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index d5154a64..9f46b384 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -489,6 +489,9 @@ centralWidget->setLayout(centralWidgetLayout);*/ if (TSystem::doesExistFileOrLevel(TFilePath(ffmpegCachePath))) { TSystem::rmDirTree(TFilePath(ffmpegCachePath)); } + + connect(TApp::instance(), SIGNAL(activeViewerChanged()), this, + SLOT(onActiveViewerChanged())); } //----------------------------------------------------------------------------- @@ -1358,6 +1361,27 @@ void MainWindow::onUpdateCheckerDone(bool error) { disconnect(m_updateChecker); m_updateChecker->deleteLater(); } + +//----------------------------------------------------------------------------- + +void MainWindow::onActiveViewerChanged() { + // sync the command state to the button state of the activated viewer + SceneViewer *activeViewer = TApp::instance()->getActiveViewer(); + if (!activeViewer) return; + BaseViewerPanel *bvp = qobject_cast( + activeViewer->parentWidget()->parentWidget()); + if (!bvp) return; + bool prev, subCamPrev; + bvp->getPreviewButtonStates(prev, subCamPrev); + + CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->setChecked(prev); + CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->setChecked(subCamPrev); +} + //----------------------------------------------------------------------------- void MainWindow::closeEvent(QCloseEvent *event) { @@ -2176,6 +2200,12 @@ void MainWindow::defineActions() { createMenuRenderAction(MI_SavePreviewedFrames, QT_TR_NOOP("&Save Previewed Frames"), "", "save_previewed_frames", tr("Save the images created during preview to a specified location.")); + createToggle(MI_ToggleViewerPreview, QT_TR_NOOP("Toggle Viewer Preview"), "", + false, MenuRenderCommandType, "pane_preview"); + createToggle(MI_ToggleViewerSubCameraPreview, + QT_TR_NOOP("Toggle Viewer Sub-camera Preview"), "", false, + MenuRenderCommandType, "pane_subpreview"); + createMenuRenderAction(MI_SaveAndRender, QT_TR_NOOP("&Save and Render"), "", "render", tr("Saves the current scene and renders according to the settings and " "location set in Output Settings.")); diff --git a/toonz/sources/toonz/mainwindow.h b/toonz/sources/toonz/mainwindow.h index 0dfcc57a..1a76138a 100644 --- a/toonz/sources/toonz/mainwindow.h +++ b/toonz/sources/toonz/mainwindow.h @@ -246,6 +246,7 @@ protected slots: void onInk1CheckTriggered(bool on); void onUpdateCheckerDone(bool); + void onActiveViewerChanged(); void toggleStatusBar(bool); void toggleTransparency(bool); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index eaa44edc..124b6696 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -64,6 +64,8 @@ #define MI_FreezePreview "MI_FrezzePreview" #define MI_SavePreviewedFrames "MI_SavePreviewedFrames" // #define MI_SavePreview "MI_SavePreview" +#define MI_ToggleViewerPreview "MI_ToggleViewerPreview" +#define MI_ToggleViewerSubCameraPreview "MI_ToggleViewerSubCameraPreview" #define MI_Print "MI_Print" #define MI_Preferences "MI_Preferences" #define MI_SavePreset "MI_SavePreset" diff --git a/toonz/sources/toonz/pane.cpp b/toonz/sources/toonz/pane.cpp index 8444e1ee..5b2a7ce2 100644 --- a/toonz/sources/toonz/pane.cpp +++ b/toonz/sources/toonz/pane.cpp @@ -44,6 +44,7 @@ #include extern TEnv::StringVar EnvSafeAreaName; +extern TEnv::IntVar EnvViewerPreviewBehavior; extern TEnv::IntVar CameraViewTransparency; extern TEnv::IntVar ShowRuleOfThirds; extern TEnv::IntVar ShowGoldenRatio; @@ -475,6 +476,58 @@ void TPanelTitleBarButtonForGrids::mousePressEvent(QMouseEvent *e) { m_menu->exec(e->globalPos() + QPoint(-100, 12)); } +//============================================================================= +// TPanelTitleBarButtonForPreview +//----------------------------------------------------------------------------- + +void TPanelTitleBarButtonForPreview::mousePressEvent(QMouseEvent *e) { + if (e->button() != Qt::RightButton) { + m_pressed = !m_pressed; + emit toggled(m_pressed); + update(); + } +} + +//----------------------------------------------------------------------------- + +void TPanelTitleBarButtonForPreview::contextMenuEvent(QContextMenuEvent *e) { + QMenu menu(this); + + // 0: current frame + // 1: all frames in the preview range + // 2: selected cell, auto play once & stop + QStringList behaviorsStrList = {tr("Current frame"), + tr("All preview range frames"), + tr("Selected cells - Auto play")}; + + QActionGroup *behaviorGroup = new QActionGroup(this); + + for (int i = 0; i < behaviorsStrList.size(); i++) { + QAction *action = menu.addAction(behaviorsStrList.at(i)); + action->setData(i); + connect(action, SIGNAL(triggered()), this, SLOT(onSetPreviewBehavior())); + action->setCheckable(true); + behaviorGroup->addAction(action); + if (i == EnvViewerPreviewBehavior) action->setChecked(true); + } + + menu.exec(e->globalPos()); +} + +//----------------------------------------------------------------------------- + +void TPanelTitleBarButtonForPreview::onSetPreviewBehavior() { + int behaviorId = qobject_cast(sender())->data().toInt(); + // change safearea if the different one is selected + if (EnvViewerPreviewBehavior != behaviorId) { + EnvViewerPreviewBehavior = behaviorId; + // emit sceneChanged without setting dirty flag + TApp::instance()->getCurrentScene()->notifySceneChanged(false); + } +} + +//----------------------------------------------------------------------------- + //============================================================================= // TPanelTitleBarButtonSet //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/pane.h b/toonz/sources/toonz/pane.h index e9387401..031363b3 100644 --- a/toonz/sources/toonz/pane.h +++ b/toonz/sources/toonz/pane.h @@ -5,7 +5,7 @@ // TODO: cambiare il nome del file in tpanel.h -//#include +// #include #include "../toonzqt/tdockwindows.h" class TPanelTitleBarButtonSet; @@ -75,7 +75,7 @@ signals: }; //----------------------------------------------------------------------------- -/*! specialized button for sage area which enables to choose safe area size by +/*! specialized button for safe area which enables to choose safe area size by * context menu */ @@ -130,6 +130,27 @@ signals: void updateViewer(); }; +//----------------------------------------------------------------------------- +/*! specialized button for safe area which enables to choose safe area size by + * context menu + */ + +class TPanelTitleBarButtonForPreview final : public TPanelTitleBarButton { + Q_OBJECT +public: + TPanelTitleBarButtonForPreview(QWidget *parent, + const QString &standardPixmapName) + : TPanelTitleBarButton(parent, standardPixmapName) {} + + bool isChecked() { return m_pressed; } + +protected: + void contextMenuEvent(QContextMenuEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; +protected slots: + void onSetPreviewBehavior(); +}; + //----------------------------------------------------------------------------- //! a buttonset can group different TPanelTitleBarButton diff --git a/toonz/sources/toonz/previewer.cpp b/toonz/sources/toonz/previewer.cpp index 32d1e706..39eb6890 100644 --- a/toonz/sources/toonz/previewer.cpp +++ b/toonz/sources/toonz/previewer.cpp @@ -196,6 +196,9 @@ public: // are assumed correct. void refreshFrame(int frame); + void addRenderData(std::vector &datas, int frame); + void addFramesToRenderQueue(const std::vector frames); + // TRenderPort methods void onRenderRasterStarted(const RenderData &renderData) override; void onRenderRasterCompleted(const RenderData &renderData) override; @@ -709,21 +712,33 @@ void Previewer::Imp::doOnRenderRasterCompleted(const RenderData &renderData) { it->second .m_rectUnderRender); // Extract may MODIFY IT! E.g. with shrinks..! cachedRas = cachedRas->extract(rectUnderRender); + if (cachedRas) { cachedRas->copy(ras); - TImageCache::instance()->add(str, ri); } - // Update the FrameInfo - it->second.m_renderedRegion += toQRect(it->second.m_rectUnderRender); - it->second.m_rectUnderRender = TRect(); + // Submit the image to the cache, for all cluster's frames + unsigned int i, size = renderData.m_frames.size(); + for (i = 0; i < size; ++i) { + int f = renderData.m_frames[i]; + std::map::iterator f_it = m_frames.find(f); + if (f_it == m_frames.end()) continue; - // Update the progress bar status - if (frame < m_pbStatus.size()) - m_pbStatus[frame] = FlipSlider::PBFrameFinished; + if (cachedRas) { + std::string f_str = m_cachePrefix + std::to_string(f); + TImageCache::instance()->add(f_str, ri); + } - // Notify listeners - notifyCompleted(frame); + // Update the FrameInfo + f_it->second.m_renderedRegion += toQRect(f_it->second.m_rectUnderRender); + f_it->second.m_rectUnderRender = TRect(); + + // Update the progress bar status + if (f < m_pbStatus.size()) m_pbStatus[f] = FlipSlider::PBFrameFinished; + + // Notify listeners + notifyCompleted(f); + } } //----------------------------------------------------------------------------- @@ -977,6 +992,69 @@ void Previewer::Imp::saveFrame() { savedFrames = 0; } +//----------------------------------------------------------------------------- + +void Previewer::Imp::addRenderData(std::vector &datas, + int frame) { + // Build the TFxPair to be passed to TRenderer + TFxPair fxPair = buildSceneFx(frame); + + // Update the RenderInfos associated with frame + m_frames[frame].m_rectUnderRender = m_previewRect; + m_frames[frame].m_alias = fxPair.m_frameA->getAlias(frame, m_renderSettings); + if (fxPair.m_frameB) + m_frames[frame].m_alias = + m_frames[frame].m_alias + + fxPair.m_frameB->getAlias(frame, m_renderSettings); + + // Retrieve the renderId of the rendering instance + m_frames[frame].m_renderId = m_renderer.nextRenderId(); + std::string contextName("P"); + contextName += m_subcamera ? "SC" : "FU"; + contextName += std::to_string(frame); + TPassiveCacheManager::instance()->setContextName(m_frames[frame].m_renderId, + contextName); + + datas.push_back(TRenderer::RenderData(frame, m_renderSettings, fxPair)); +} + +//----------------------------------------------------------------------------- + +void Previewer::Imp::addFramesToRenderQueue(const std::vector frames) { + if (suspendedRendering) return; + // Build the region to render + updatePreviewRect(); + if (m_previewRect.getLx() <= 0 || m_previewRect.getLy() <= 0) return; + + RenderDataVector *renderDatas = new RenderDataVector; + + for (const auto &f : frames) { + std::map::iterator it = m_frames.find(f); + if (it == m_frames.end()) { + it = m_frames.insert(std::make_pair(f, FrameInfo())).first; + // In case the frame is not in the frame range, we add a temporary + // supplement + // to the progress bar. + if (f >= (int)m_pbStatus.size()) m_pbStatus.resize(f + 1); + addRenderData(*renderDatas, f); + } else if (f < m_pbStatus.size() && + m_pbStatus[f] == FlipSlider::PBFrameNotStarted) { + // In case the rect we would render is contained in the frame's rendered + // region, quit + if (::contains(it->second.m_renderedRegion, m_previewRect)) return; + // Then, check the m_previewRect against the frame's m_rectUnderRendering. + // Ensure that we're not re-launching the very same render. + if (it->second.m_rectUnderRender == m_previewRect) return; + // Stop any frame's previously running render process + m_renderer.abortRendering(it->second.m_renderId); + addRenderData(*renderDatas, f); + } + } + + // Finally, start rendering all frames which were not found in cache + m_renderer.startRendering(renderDatas); +} + //============================================================================= // Previewer //----------------------------------------------------------------------------- @@ -1138,8 +1216,8 @@ void Previewer::saveRenderedFrames() { //----------------------------------------------------------------------------- -/*! Restituisce un puntatore al raster randerizzato se il frame e' disponibile, - altrimenti comincia a calcolarlo*/ +/*! Returns a pointer to the rendered raster if the frame is available, + otherwise start calculating it */ TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const { if (frame < 0) return TRasterP(); std::map::iterator it = m_imp->m_frames.find(frame); @@ -1184,6 +1262,13 @@ TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const { //----------------------------------------------------------------------------- +void Previewer::addFramesToRenderQueue(const std::vector frames) const { + if (suspendedRendering) return; + m_imp->addFramesToRenderQueue(frames); +} + +//----------------------------------------------------------------------------- + //! Verifica se \b frame e' nella cache, cioe' se il frame e' disponibile bool Previewer::isFrameReady(int frame) const { if (frame < 0 || frame >= (int)m_imp->m_pbStatus.size()) return false; diff --git a/toonz/sources/toonz/previewer.h b/toonz/sources/toonz/previewer.h index 566d77b1..6cb270e4 100644 --- a/toonz/sources/toonz/previewer.h +++ b/toonz/sources/toonz/previewer.h @@ -70,6 +70,7 @@ public: void removeListener(Listener *); TRasterP getRaster(int frame, bool renderIfNeeded = true) const; + void addFramesToRenderQueue(const std::vector frames) const; bool isFrameReady(int frame) const; bool doSaveRenderedFrames(TFilePath fp); diff --git a/toonz/sources/toonz/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index 2048cd04..9ece9bd7 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -12,6 +12,8 @@ #include "ruler.h" #include "locatorpopup.h" #include "../stopmotion/stopmotion.h" +#include "tenv.h" +#include "cellselection.h" // TnzTools includes #include "tools/cursors.h" @@ -59,6 +61,7 @@ #include "toonz/toonzimageutils.h" #include "toonz/txshleveltypes.h" #include "subcameramanager.h" +#include "toutputproperties.h" // TnzCore includes #include "tpalette.h" @@ -96,6 +99,11 @@ TEnv::IntVar ShowSymmetryGuide("ShowSymmetryGuide", 1); void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d, double pixelSize); +// 0: current frame +// 1: all frames in the preview range +// 2: selected cell, auto play once & stop +TEnv::IntVar EnvViewerPreviewBehavior("ViewerPreviewBehavior", 0); + //------------------------------------------------------------------------------- namespace { @@ -968,16 +976,47 @@ void SceneViewer::enablePreview(int previewMode) { Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW) ->removeListener(this); + m_previewMode = previewMode; + // Schedule as a listener to Previewer. - if (previewMode != NO_PREVIEW) { + if (m_previewMode != NO_PREVIEW) { Previewer *previewer = - Previewer::instance(previewMode == SUBCAMERA_PREVIEW); + Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW); + previewer->addListener(this); + // 0: current frame + // 1: all frames in the preview range + // 2: selected cell, auto play once & stop + if (EnvViewerPreviewBehavior == 1) { + int r0, r1, step; + ToonzScene *scene = app->getCurrentScene()->getScene(); + scene->getProperties()->getPreviewProperties()->getRange(r0, r1, step); + if (r0 > r1) { + r0 = 0; + r1 = scene->getFrameCount() - 1; + } + int currentFrame = app->getCurrentFrame()->getFrame(); + std::vector queueFrames; + for (int f = currentFrame; f <= r1; f += step) queueFrames.push_back(f); + for (int f = r0; f < currentFrame; f += step) queueFrames.push_back(f); + + previewer->addFramesToRenderQueue(queueFrames); + } else if (EnvViewerPreviewBehavior == 2) { + TCellSelection *cellSel = + dynamic_cast(TSelection::getCurrent()); + if (cellSel && !cellSel->isEmpty()) { + int r0, c0, r1, c1; + cellSel->getSelectedCells(r0, c0, r1, c1); + if (r0 < r1) { + std::vector queueFrames; + for (int f = r0; f <= r1; f++) queueFrames.push_back(f); + previewer->addFramesToRenderQueue(queueFrames); + } + } + } previewer->update(); } - m_previewMode = previewMode; - GLInvalidateAll(); // for updating the title bar @@ -1043,7 +1082,8 @@ void SceneViewer::showEvent(QShowEvent *) { m_visualSettings.m_sceneProperties = TApp::instance()->getCurrentScene()->getScene()->getProperties(); - // If the viewer is hidden and preview is activated, remove the listener from preview + // If the viewer is hidden and preview is activated, remove the listener from + // preview if (m_previewMode != NO_PREVIEW) Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)->addListener(this); @@ -1053,7 +1093,7 @@ void SceneViewer::showEvent(QShowEvent *) { bool ret = connect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(resetSceneViewer())); ret = ret && connect(sceneHandle, SIGNAL(sceneChanged()), this, - SLOT(onSceneChanged())); + SLOT(onSceneChanged())); TFrameHandle *frameHandle = app->getCurrentFrame(); ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, @@ -1127,7 +1167,8 @@ void SceneViewer::showEvent(QShowEvent *) { //----------------------------------------------------------------------------- void SceneViewer::hideEvent(QHideEvent *) { - // If the viewer is hidden and preview is activated, remove the listener from preview + // If the viewer is hidden and preview is activated, remove the listener from + // preview if (m_previewMode != NO_PREVIEW) Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW) ->removeListener(this); @@ -1981,7 +2022,7 @@ static void drawFpsGraph(int t0, int t1) { //----------------------------------------------------------------------------- -//#define FPS_HISTOGRAM +// #define FPS_HISTOGRAM void SceneViewer::paintGL() { #ifdef _DEBUG @@ -2849,9 +2890,9 @@ void SceneViewer::fitToCamera() { TPointD P01 = cameraAff * cameraRect.getP01(); TPointD P11 = cameraAff * cameraRect.getP11(); TPointD p0 = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}), - std::min({P00.y, P01.y, P10.y, P11.y})); + std::min({P00.y, P01.y, P10.y, P11.y})); TPointD p1 = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}), - std::max({P00.y, P01.y, P10.y, P11.y})); + std::max({P00.y, P01.y, P10.y, P11.y})); cameraRect = TRectD(p0.x, p0.y, p1.x, p1.y); // Pan @@ -2894,9 +2935,9 @@ void SceneViewer::fitToCameraOutline() { TPointD P01 = cameraAff * cameraRect.getP01(); TPointD P11 = cameraAff * cameraRect.getP11(); TPointD p0 = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}), - std::min({P00.y, P01.y, P10.y, P11.y})); + std::min({P00.y, P01.y, P10.y, P11.y})); TPointD p1 = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}), - std::max({P00.y, P01.y, P10.y, P11.y})); + std::max({P00.y, P01.y, P10.y, P11.y})); cameraRect = TRectD(p0.x, p0.y, p1.x, p1.y); // Pan diff --git a/toonz/sources/toonz/subcameramanager.cpp b/toonz/sources/toonz/subcameramanager.cpp index d3d941bd..21d4852a 100644 --- a/toonz/sources/toonz/subcameramanager.cpp +++ b/toonz/sources/toonz/subcameramanager.cpp @@ -31,6 +31,11 @@ inline bool bitwiseContains(UCHAR flag, UCHAR state) { inline bool bitwiseExclude(UCHAR flag, UCHAR state) { return bitwiseContains(~state, flag); } + +inline bool areNear(double v0, double v1, double thres = 20.0) { + return std::abs(v0 - v1) < thres; +} + } // namespace //******************************************************************************** @@ -147,6 +152,27 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, TPointD worldCurPos(viewer->winToWorld(curPos)); TApp *app = TApp::instance(); + + TCamera *camera = app->getCurrentScene()->getScene()->getCurrentCamera(); + TRectD cameraStageRect(camera->getCameraToStageRef() * + convert(TRect(camera->getRes()))); + + // Snap to the current camera frame + // horizontal + if (worldCurPos.x < worldMousePressPos.x && + areNear(worldCurPos.x, cameraStageRect.x0)) + worldCurPos.x = cameraStageRect.x0; + else if (worldCurPos.x > worldMousePressPos.x && + areNear(worldCurPos.x, cameraStageRect.x1)) + worldCurPos.x = cameraStageRect.x1; + // vertical + if (worldCurPos.y < worldMousePressPos.y && + areNear(worldCurPos.y, cameraStageRect.y0)) + worldCurPos.y = cameraStageRect.y0; + else if (worldCurPos.y > worldMousePressPos.y && + areNear(worldCurPos.y, cameraStageRect.y1)) + worldCurPos.y = cameraStageRect.y1; + TAffine cameraAffInv( app->getCurrentXsheet() ->getXsheet() @@ -162,15 +188,17 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, std::max(worldMousePressPos.x, worldCurPos.x), std::max(worldMousePressPos.y, worldCurPos.y)); - TCamera *camera = app->getCurrentScene()->getScene()->getCurrentCamera(); // camera->setInterestStageRect(worldPreviewSubCameraRect); TRectD previewSubCameraD(camera->getStageToCameraRef() * worldPreviewSubCameraRect); m_editingInterestRect = TRect(previewSubCameraD.x0, previewSubCameraD.y0, - previewSubCameraD.x1 - 1, previewSubCameraD.y1 - 1) * - TRect(camera->getRes()); + previewSubCameraD.x1 - 1, previewSubCameraD.y1 - 1); + // m_editingInterestRect = + // TRect(previewSubCameraD.x0, previewSubCameraD.y0, + // previewSubCameraD.x1 - 1, previewSubCameraD.y1 - 1) * + // TRect(camera->getRes()); viewer->update(); } else { @@ -194,7 +222,8 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, subRect.y1 = subRect.y1 + dragDistance.y; } - m_editingInterestRect = subRect * TRect(camera->getRes()); + m_editingInterestRect = subRect; + // m_editingInterestRect = subRect * TRect(camera->getRes()); viewer->update(); } @@ -301,11 +330,32 @@ UCHAR PreviewSubCameraManager::getSubCameraDragEnum(SceneViewer *viewer, //----------------------------------------------------------------------------- -TPoint PreviewSubCameraManager::getSubCameraDragDistance( - SceneViewer *viewer, const QPointF &mousePos) { +TPoint PreviewSubCameraManager::getSubCameraDragDistance(SceneViewer *viewer, + QPointF &mousePos) { // Build the camera drag distance if (m_clickAndDrag) return TPoint(); + // Snap to the current camera frame + TCamera *camera = + TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera(); + if (!bitwiseExclude(m_dragType, OUTER)) { + TPointD btmLft(cameraToWin(viewer, TPointD(0, 0))); + TPointD tpRght( + cameraToWin(viewer, TPointD(camera->getRes().lx, camera->getRes().ly))); + if (bitwiseContains(m_dragType, DRAG_LEFT) && + areNear(mousePos.x(), btmLft.x)) + mousePos.setX(btmLft.x); + else if (bitwiseContains(m_dragType, DRAG_RIGHT) && + areNear(mousePos.x(), tpRght.x)) + mousePos.setX(tpRght.x); + if (bitwiseContains(m_dragType, DRAG_BOTTOM) && + areNear(mousePos.y(), (double)viewer->height() - btmLft.y)) + mousePos.setY((double)viewer->height() - btmLft.y); + else if (bitwiseContains(m_dragType, DRAG_TOP) && + areNear(mousePos.y(), (double)viewer->height() - tpRght.y)) + mousePos.setY((double)viewer->height() - tpRght.y); + } + TPointD cameraMousePos(winToCamera(viewer, mousePos)); if (bitwiseExclude(m_dragType, OUTER)) { @@ -313,8 +363,6 @@ TPoint PreviewSubCameraManager::getSubCameraDragDistance( return TPoint(resultD.x, resultD.y); } - TCamera *camera = - TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera(); TRect subCamera = camera->getInterestRect(); TRectD subCameraD(subCamera.x0, subCamera.y0, subCamera.x1 + 1, subCamera.y1 + 1); diff --git a/toonz/sources/toonz/subcameramanager.h b/toonz/sources/toonz/subcameramanager.h index 3f89a8cc..778ce0c8 100644 --- a/toonz/sources/toonz/subcameramanager.h +++ b/toonz/sources/toonz/subcameramanager.h @@ -110,7 +110,7 @@ private: TPointD cameraToWin(SceneViewer *viewer, const TPointD &cameraPos) const; UCHAR getSubCameraDragEnum(SceneViewer *viewer, const QPointF &mousePos); - TPoint getSubCameraDragDistance(SceneViewer *viewer, const QPointF &mousePos); + TPoint getSubCameraDragDistance(SceneViewer *viewer, QPointF &mousePos); }; #endif // SUBCAMERAMANAGER_INCLUDED diff --git a/toonz/sources/toonz/viewerpane.cpp b/toonz/sources/toonz/viewerpane.cpp index cb1426c8..7c1db663 100644 --- a/toonz/sources/toonz/viewerpane.cpp +++ b/toonz/sources/toonz/viewerpane.cpp @@ -44,6 +44,8 @@ #include "xsheetdragtool.h" #include "ruler.h" #include "menubarcommandids.h" +#include "tenv.h" +#include "cellselection.h" // Qt includes #include @@ -66,6 +68,7 @@ using namespace DVGui; +extern TEnv::IntVar EnvViewerPreviewBehavior; //============================================================================= // // BaseViewerPanel @@ -140,7 +143,7 @@ BaseViewerPanel::BaseViewerPanel(QWidget *parent, Qt::WindowFlags flags) this, SLOT(onButtonPressed(FlipConsole::EGadget))); ret = ret && connect(m_sceneViewer, SIGNAL(previewStatusChanged()), this, - SLOT(update())); + SLOT(onPreviewStatusChanged())); ret = ret && connect(m_sceneViewer, SIGNAL(onFlipHChanged(bool)), this, SLOT(setFlipHButtonChecked(bool))); ret = ret && connect(m_sceneViewer, SIGNAL(onFlipVChanged(bool)), this, @@ -149,6 +152,9 @@ BaseViewerPanel::BaseViewerPanel(QWidget *parent, Qt::WindowFlags flags) ret = ret && connect(app->getCurrentScene(), SIGNAL(sceneSwitched()), this, SLOT(onSceneSwitched())); + ret = ret && connect(app, SIGNAL(activeViewerChanged()), this, + SLOT(onActiveViewerChanged())); + assert(ret); UINT mask = 0; @@ -269,7 +275,7 @@ void BaseViewerPanel::onDrawFrame(int frame, } // assert(frame >= 0); // frame can be negative in rare cases - if (frame != frameHandle->getFrameIndex() + 1) { + if (frame != frameHandle->getFrameIndex() + 1 && !settings.m_drawBlankFrame) { int oldFrame = frameHandle->getFrame(); frameHandle->setCurrentFrame(frame); if (!frameHandle->isPlaying() && !frameHandle->isEditingLevel() && @@ -462,30 +468,51 @@ void BaseViewerPanel::initializeTitleBar(TPanelTitleBar *titleBar) { SLOT(freeze(bool))); // preview toggles - m_previewButton = new TPanelTitleBarButton( + m_previewButton = new TPanelTitleBarButtonForPreview( titleBar, getIconThemePath("actions/20/pane_preview.svg")); x += 10 + iconWidth; titleBar->add(QPoint(x, 0), m_previewButton); m_previewButton->setToolTip(tr("Preview")); - ret = ret && connect(m_previewButton, SIGNAL(toggled(bool)), - SLOT(enableFullPreview(bool))); - m_subcameraPreviewButton = new TPanelTitleBarButton( + // ret = ret && connect(m_previewButton, SIGNAL(toggled(bool)), + // SLOT(enableFullPreview(bool))); + + m_subcameraPreviewButton = new TPanelTitleBarButtonForPreview( titleBar, getIconThemePath("actions/20/pane_subpreview.svg")); x += 1 + 24; // width of pane_preview.svg = 24px titleBar->add(QPoint(x, 0), m_subcameraPreviewButton); m_subcameraPreviewButton->setToolTip(tr("Sub-camera Preview")); - ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), - SLOT(enableSubCameraPreview(bool))); + + // ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), + // SLOT(enableSubCameraPreview(bool))); assert(ret); } //----------------------------------------------------------------------------- +void BaseViewerPanel::getPreviewButtonStates(bool &prev, bool &subCamPrev) { + prev = m_previewButton->isChecked(); + subCamPrev = m_subcameraPreviewButton->isChecked(); +} + +//----------------------------------------------------------------------------- + void BaseViewerPanel::enableFullPreview(bool enabled) { m_subcameraPreviewButton->setPressed(false); + if (CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->isChecked()) + CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->setChecked(false); + + if (!enabled && EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + TApp::instance()->getCurrentFrame()->isPlaying()) + CommandManager::instance()->execute(MI_Pause); + m_sceneViewer->enablePreview(enabled ? SceneViewer::FULL_PREVIEW : SceneViewer::NO_PREVIEW); m_flipConsole->setProgressBarStatus( @@ -497,6 +524,18 @@ void BaseViewerPanel::enableFullPreview(bool enabled) { void BaseViewerPanel::enableSubCameraPreview(bool enabled) { m_previewButton->setPressed(false); + if (CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->isChecked()) + CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->setChecked(false); + + if (!enabled && EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + TApp::instance()->getCurrentFrame()->isPlaying()) + CommandManager::instance()->execute(MI_Pause); + m_sceneViewer->enablePreview(enabled ? SceneViewer::SUBCAMERA_PREVIEW : SceneViewer::NO_PREVIEW); m_flipConsole->setProgressBarStatus( @@ -542,6 +581,26 @@ void BaseViewerPanel::onPlayingStatusChanged(bool playing) { m_playing = false; m_first = true; } + + // if preview behavior mode is "selected cells", release preview mode when + // stopped + if (!playing && EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + !Previewer::instance(m_sceneViewer->getPreviewMode() == + SceneViewer::SUBCAMERA_PREVIEW) + ->isBusy()) { + if (CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->isChecked()) + CommandManager::instance()->getAction(MI_ToggleViewerPreview)->trigger(); + else if (CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->isChecked()) + CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->trigger(); + } + if (Preferences::instance()->getOnionSkinDuringPlayback()) return; OnionSkinMask osm = TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask(); @@ -870,6 +929,90 @@ void BaseViewerPanel::load(QSettings &settings) { settings.value("consoleParts", mask).toUInt()); } +//----------------------------------------------------------------------------- + +void BaseViewerPanel::onPreviewStatusChanged() { + // if preview behavior mode is "selected cells", play once the all frames are + // completed + if (EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + !TApp::instance()->getCurrentFrame()->isPlaying() && + m_sceneViewer->isPreviewEnabled() && + !Previewer::instance(m_sceneViewer->getPreviewMode() == + SceneViewer::SUBCAMERA_PREVIEW) + ->isBusy()) { + TCellSelection *cellSel = + dynamic_cast(TSelection::getCurrent()); + if (cellSel && !cellSel->isEmpty()) { + int r0, c0, r1, c1; + cellSel->getSelectedCells(r0, c0, r1, c1); + if (r0 < r1) { + // check if all frame range is rendered. this check is needed since + // isBusy() will not be true just after the preview is triggered + for (int r = r0; r <= r1; r++) { + if (!Previewer::instance(m_sceneViewer->getPreviewMode() == + SceneViewer::SUBCAMERA_PREVIEW) + ->isFrameReady(r)) { + update(); + return; + } + } + m_flipConsole->setStopAt(r1 + 1); + m_flipConsole->setStartAt(r0 + 1); + TApp::instance()->getCurrentFrame()->setFrame(r0); + CommandManager::instance()->execute(MI_Loop); + } + } + } + + update(); +} + +//----------------------------------------------------------------------------- +// sync preview commands and buttons states when the viewer becomes active + +void BaseViewerPanel::onActiveViewerChanged() { + bool ret = true; + if (TApp::instance()->getActiveViewer() == m_sceneViewer) { + ret = ret && + connect(m_previewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction(MI_ToggleViewerPreview), + SLOT(trigger())); + ret = ret && + connect(CommandManager::instance()->getAction(MI_ToggleViewerPreview), + SIGNAL(triggered(bool)), m_previewButton, + SLOT(setPressed(bool))); + ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SLOT(trigger())); + ret = ret && connect(CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SIGNAL(triggered(bool)), m_subcameraPreviewButton, + SLOT(setPressed(bool))); + m_isActive = true; + } else if (m_isActive) { + ret = ret && disconnect(m_previewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction( + MI_ToggleViewerPreview), + SLOT(trigger())); + ret = ret && + disconnect( + CommandManager::instance()->getAction(MI_ToggleViewerPreview), + SIGNAL(triggered(bool)), m_previewButton, SLOT(setPressed(bool))); + ret = ret && disconnect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SLOT(trigger())); + ret = ret && disconnect(CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SIGNAL(triggered(bool)), m_subcameraPreviewButton, + SLOT(setPressed(bool))); + m_isActive = false; + } + assert(ret); +} + //============================================================================= // // SceneViewerPanel @@ -912,4 +1055,45 @@ void SceneViewerPanel::checkOldVersionVisblePartsFlags(QSettings &settings) { settings.value("visibleParts", m_visiblePartsFlag).toUInt(); settings.remove("visibleParts"); settings.setValue("viewerVisibleParts", m_visiblePartsFlag); -} \ No newline at end of file +} + +//========================================================= + +class ViewerPreviewCommands : public QObject { +public: + ViewerPreviewCommands() { + setCommandHandler("MI_ToggleViewerPreview", this, + &ViewerPreviewCommands::onPreview); + setCommandHandler("MI_ToggleViewerSubCameraPreview", this, + &ViewerPreviewCommands::onSubCameraPreview); + } + + void onPreview(); + void onSubCameraPreview(); +}; + +void ViewerPreviewCommands::onPreview() { + SceneViewer *activeViewer = TApp::instance()->getActiveViewer(); + if (!activeViewer) return; + BaseViewerPanel *bvp = qobject_cast( + activeViewer->parentWidget()->parentWidget()); + if (!bvp) return; + bool on = CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->isChecked(); + bvp->enableFullPreview(on); +} + +void ViewerPreviewCommands::onSubCameraPreview() { + SceneViewer *activeViewer = TApp::instance()->getActiveViewer(); + if (!activeViewer) return; + BaseViewerPanel *bvp = qobject_cast( + activeViewer->parentWidget()->parentWidget()); + if (!bvp) return; + bool on = CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->isChecked(); + bvp->enableSubCameraPreview(on); +} + +ViewerPreviewCommands viewerPreviewCommands; diff --git a/toonz/sources/toonz/viewerpane.h b/toonz/sources/toonz/viewerpane.h index bda5bef1..84148b39 100644 --- a/toonz/sources/toonz/viewerpane.h +++ b/toonz/sources/toonz/viewerpane.h @@ -51,8 +51,8 @@ protected: FlipConsole *m_flipConsole; ViewerKeyframeNavigator *m_keyFrameButton; TPanelTitleBarButtonSet *m_referenceModeBs; - TPanelTitleBarButton *m_previewButton; - TPanelTitleBarButton *m_subcameraPreviewButton; + TPanelTitleBarButtonForPreview *m_previewButton; + TPanelTitleBarButtonForPreview *m_subcameraPreviewButton; bool m_onionSkinActive = false; UINT m_visiblePartsFlag; bool m_playSound = true; @@ -64,6 +64,8 @@ protected: bool m_first = true; TSoundTrack *m_sound = NULL; + bool m_isActive = false; + public: BaseViewerPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); ~BaseViewerPanel() {} @@ -91,6 +93,8 @@ public: void initializeTitleBar(TPanelTitleBar *titleBar); + void getPreviewButtonStates(bool &prev, bool &subCamPrev); + protected: // void contextMenuEvent(QContextMenuEvent *event) override; void showEvent(QShowEvent *) override; @@ -111,6 +115,8 @@ public slots: void onButtonPressed(FlipConsole::EGadget button); void setFlipHButtonChecked(bool checked); void setFlipVButtonChecked(bool checked); + void enableFullPreview(bool enabled); + void enableSubCameraPreview(bool enabled); void changeSceneFps(int value); protected slots: @@ -121,8 +127,8 @@ protected slots: void onPlayingStatusChanged(bool playing); // for showing/hiding the parts void onShowHideActionTriggered(QAction *); - void enableFullPreview(bool enabled); - void enableSubCameraPreview(bool enabled); + void onPreviewStatusChanged(); + void onActiveViewerChanged(); }; class SceneViewerPanel final : public BaseViewerPanel { diff --git a/toonz/sources/toonzlib/tcamera.cpp b/toonz/sources/toonzlib/tcamera.cpp index 99d68c9a..5710c5dd 100644 --- a/toonz/sources/toonzlib/tcamera.cpp +++ b/toonz/sources/toonzlib/tcamera.cpp @@ -11,9 +11,7 @@ TCamera::TCamera() //: m_size(12, 9), m_res(768, 576), m_xPrevalence(true) //: m_size(36, 20.25), - : m_size(16, 9), - m_res(1920, 1080), - m_xPrevalence(true) {} + : m_size(16, 9), m_res(1920, 1080), m_xPrevalence(true) {} //------------------------------------------------------------------- @@ -110,15 +108,18 @@ TRectD TCamera::getStageRect() const { //------------------------------------------------------------------- void TCamera::setInterestRect(const TRect &rect) { - // Not using the TRect's common intersection. Unfortunately, in case - // the rect's coordinates have lx or ly < 0, the intersection returns - // the default (empty) rect. We want to maintain the coordinates instead. - // m_interestRect = rect * TRect(m_res); - - m_interestRect.x0 = std::max(rect.x0, 0); - m_interestRect.y0 = std::max(rect.y0, 0); - m_interestRect.x1 = std::min(rect.x1, m_res.lx - 1); - m_interestRect.y1 = std::min(rect.y1, m_res.ly - 1); + // enable to preview outside of the original camera rect + m_interestRect = rect; + return; + //// Not using the TRect's common intersection. Unfortunately, in case + //// the rect's coordinates have lx or ly < 0, the intersection returns + //// the default (empty) rect. We want to maintain the coordinates instead. + //// m_interestRect = rect * TRect(m_res); + // + // m_interestRect.x0 = std::max(rect.x0, 0); + // m_interestRect.y0 = std::max(rect.y0, 0); + // m_interestRect.x1 = std::min(rect.x1, m_res.lx - 1); + // m_interestRect.y1 = std::min(rect.y1, m_res.ly - 1); } //------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/flipconsole.cpp b/toonz/sources/toonzqt/flipconsole.cpp index ab32602f..79071624 100644 --- a/toonz/sources/toonzqt/flipconsole.cpp +++ b/toonz/sources/toonzqt/flipconsole.cpp @@ -821,9 +821,11 @@ void FlipConsole::onNextFrame(int fps, QElapsedTimer *timer, else m_fpsField->setLineEditBackgroundColor(Qt::red); } - if (m_stopAt > 0 && m_currentFrame >= m_stopAt) { + if (m_stopAt > 0 && m_currentFrame >= m_stopAt && + (m_isPlay || m_startAt == -1)) { doButtonPressed(ePause); - m_stopAt = -1; + m_stopAt = -1; + m_startAt = -1; } } @@ -849,6 +851,10 @@ void FlipConsole::playNextFrame(QElapsedTimer *timer, qint64 targetInstant) { int from = m_from, to = m_to; if (m_markerFrom <= m_markerTo && m_stopAt == -1) from = m_markerFrom, to = m_markerTo; + else if (m_stopAt > 0 && m_startAt > 0) { + from = m_startAt; + to = m_stopAt; + } if (m_framesCount == 0 || (m_isPlay && m_currentFrame == (m_reverse ? from : to))) { @@ -1577,6 +1583,7 @@ void FlipConsole::onButtonPressed(int button) { playingConsole->setChecked(ePause, true); stoppedOther = true; m_stopAt = -1; + m_startAt = -1; } } if (stoppedOther) { @@ -1732,7 +1739,8 @@ void FlipConsole::doButtonPressed(UINT button) { playingConsole->setChecked(ePause, true); } } - m_stopAt = -1; + m_stopAt = -1; + m_startAt = -1; return; } @@ -1740,6 +1748,7 @@ void FlipConsole::doButtonPressed(UINT button) { if (m_playbackExecutor.isRunning()) m_playbackExecutor.abort(); m_stopAt = -1; + m_startAt = -1; m_isPlay = false; m_blanksToDraw = 0; @@ -1885,6 +1894,10 @@ void FlipConsole::setStopAt(int frame) { m_stopAt = frame; } //-------------------------------------------------------------------- +void FlipConsole::setStartAt(int frame) { m_startAt = frame; } + +//-------------------------------------------------------------------- + QFrame *FlipConsole::createFrameSlider() { QFrame *frameSliderFrame = new QFrame(this);