From 9e314a257d2e388aa4eada0cbf9761d3f97b9160 Mon Sep 17 00:00:00 2001 From: Rodney Date: Jan 20 2023 13:32:39 +0000 Subject: Merge pull request #4712 from shun-iwasawa/c/enhance_viewer_preview Viewer preview feature enhancement --- diff --git a/toonz/sources/include/toonzqt/flipconsole.h b/toonz/sources/include/toonzqt/flipconsole.h index 7746360..19967a5 100644 --- a/toonz/sources/include/toonzqt/flipconsole.h +++ b/toonz/sources/include/toonzqt/flipconsole.h @@ -276,6 +276,7 @@ public: setChecked(button, !isChecked(button)); } void setStopAt(int frame); + void setStartAt(int frame); // the main (currently the only) use for current flipconsole and setActive is // to @@ -356,6 +357,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 8189a51..e387677 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -467,6 +467,9 @@ centralWidget->setLayout(centralWidgetLayout);*/ if (TSystem::doesExistFileOrLevel(TFilePath(ffmpegCachePath))) { TSystem::rmDirTree(TFilePath(ffmpegCachePath)); } + + connect(TApp::instance(), SIGNAL(activeViewerChanged()), this, + SLOT(onActiveViewerChanged())); } //----------------------------------------------------------------------------- @@ -1319,6 +1322,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) { @@ -2056,6 +2080,12 @@ void MainWindow::defineActions() { createMenuRenderAction(MI_SavePreviewedFrames, QT_TR_NOOP("&Save Previewed Frames"), "", "save_previewed_frames"); + 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"); + createRightClickMenuAction(MI_OpenPltGizmo, QT_TR_NOOP("&Palette Gizmo"), "", "palettegizmo"); createRightClickMenuAction(MI_EraseUnusedStyles, @@ -2823,7 +2853,7 @@ void MainWindow::defineActions() { createToolOptionsAction("A_ToolOption_RotateRight", QT_TR_NOOP("Rotate Selection/Object Right"), ""); -// Visualization + // Visualization createViewerAction(V_ZoomIn, QT_TR_NOOP("Zoom In"), "+"); createViewerAction(V_ZoomOut, QT_TR_NOOP("Zoom Out"), "-"); diff --git a/toonz/sources/toonz/mainwindow.h b/toonz/sources/toonz/mainwindow.h index 865d0c5..8a87855 100644 --- a/toonz/sources/toonz/mainwindow.h +++ b/toonz/sources/toonz/mainwindow.h @@ -221,6 +221,7 @@ protected slots: void onInk1CheckTriggered(bool on); void onUpdateCheckerDone(bool); + void onActiveViewerChanged(); public slots: /*--- タイトルにシーン名を入れる ---*/ diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index b749dee..11fe2fe 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -55,6 +55,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 d6a6243..2f2662f 100644 --- a/toonz/sources/toonz/pane.cpp +++ b/toonz/sources/toonz/pane.cpp @@ -37,6 +37,7 @@ #include extern TEnv::StringVar EnvSafeAreaName; +extern TEnv::IntVar EnvViewerPreviewBehavior; //============================================================================= // TPanel @@ -411,6 +412,58 @@ void TPanelTitleBarButtonForSafeArea::onSetSafeArea() { //----------------------------------------------------------------------------- //============================================================================= +// 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 6cd40bc..fb809db 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; @@ -68,7 +68,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 */ @@ -88,6 +88,27 @@ protected slots: }; //----------------------------------------------------------------------------- +/*! 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 class TPanelTitleBarButtonSet final : public QObject { diff --git a/toonz/sources/toonz/previewer.cpp b/toonz/sources/toonz/previewer.cpp index 7192ebe..8ec40c6 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); + } } //----------------------------------------------------------------------------- @@ -980,6 +995,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 //----------------------------------------------------------------------------- @@ -1141,8 +1219,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); @@ -1187,6 +1265,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 566d77b..6cb270e 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 7a77705..ee9360a 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -14,6 +14,8 @@ #if defined(x64) #include "../stopmotion/stopmotion.h" #endif +#include "tenv.h" +#include "cellselection.h" // TnzTools includes #include "tools/cursors.h" @@ -61,6 +63,7 @@ #include "toonz/toonzimageutils.h" #include "toonz/txshleveltypes.h" #include "subcameramanager.h" +#include "toutputproperties.h" // TnzCore includes #include "tpalette.h" @@ -93,6 +96,11 @@ 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 { @@ -950,16 +958,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 @@ -1025,7 +1064,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); @@ -1035,7 +1075,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())); ret = ret && connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)), this, SLOT(onPreferenceChanged(const QString &))); @@ -1112,7 +1152,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); @@ -1856,7 +1897,7 @@ static void drawFpsGraph(int t0, int t1) { //----------------------------------------------------------------------------- -//#define FPS_HISTOGRAM +// #define FPS_HISTOGRAM void SceneViewer::paintGL() { #ifdef _DEBUG @@ -2670,9 +2711,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 @@ -2715,9 +2756,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 d3d941b..21d4852 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 3f89a8c..778ce0c 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 6238d77..ef1a0bc 100644 --- a/toonz/sources/toonz/viewerpane.cpp +++ b/toonz/sources/toonz/viewerpane.cpp @@ -45,6 +45,8 @@ #include "xsheetdragtool.h" #include "ruler.h" #include "menubarcommandids.h" +#include "tenv.h" +#include "cellselection.h" // Qt includes #include @@ -67,6 +69,7 @@ using namespace DVGui; +extern TEnv::IntVar EnvViewerPreviewBehavior; //============================================================================= // // BaseViewerPanel @@ -138,7 +141,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, @@ -147,6 +150,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); setFocusProxy(m_sceneViewer); @@ -256,7 +262,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() && @@ -429,30 +435,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( @@ -464,6 +491,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( @@ -509,6 +548,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(); @@ -800,6 +859,90 @@ void BaseViewerPanel::load(QSettings &settings) { updateShowHide(); } +//----------------------------------------------------------------------------- + +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 @@ -842,4 +985,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 6ec8889..953a7dd 100644 --- a/toonz/sources/toonz/viewerpane.h +++ b/toonz/sources/toonz/viewerpane.h @@ -52,8 +52,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; @@ -65,6 +65,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); protected slots: @@ -120,8 +126,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 99d68c9..5710c5d 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 5f52a7f..8972b81 100644 --- a/toonz/sources/toonzqt/flipconsole.cpp +++ b/toonz/sources/toonzqt/flipconsole.cpp @@ -830,9 +830,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; } } @@ -842,6 +844,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))) { @@ -1506,6 +1512,7 @@ void FlipConsole::onButtonPressed(int button) { playingConsole->setChecked(ePause, true); stoppedOther = true; m_stopAt = -1; + m_startAt = -1; } } if (stoppedOther) { @@ -1656,7 +1663,8 @@ void FlipConsole::doButtonPressed(UINT button) { playingConsole->setChecked(ePause, true); } } - m_stopAt = -1; + m_stopAt = -1; + m_startAt = -1; return; } @@ -1665,6 +1673,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; @@ -1808,6 +1817,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);