#include "stopmotion.h" // TnzCore includes #include "menubarcommandids.h" #include "tapp.h" #include "tenv.h" #include "tsystem.h" #include "filebrowsermodel.h" #include "tlevel_io.h" #include "toutputproperties.h" #include "filebrowserpopup.h" #include "tunit.h" #include "flipbook.h" #include "toonz/namebuilder.h" #include "toonz/preferences.h" #include "toonz/tcamera.h" #include "toonz/tcolumnhandle.h" #include "toonz/tframehandle.h" #include "toonz/levelset.h" #include "toonz/sceneproperties.h" #include "toonz/toonzscene.h" #include "toonz/tscenehandle.h" #include "toonz/stage.h" #include "toonz/txsheethandle.h" #include "toonz/txshlevelhandle.h" #include "toonz/levelproperties.h" #include "toonz/tstageobjecttree.h" #include "toonzqt/menubarcommand.h" #include "toonzqt/icongenerator.h" #include #include #include #include #include #include #include #include #include #include #include #include // Connected camera TEnv::IntVar StopMotionOpacity("StopMotionOpacity", 204); TEnv::IntVar StopMotionAlwaysLiveView("StopMotionAlwaysLiveView", 0); TEnv::IntVar StopMotionPlaceOnXSheet("StopMotionPlaceOnXSheet", 1); TEnv::IntVar StopMotionReviewTime("StopMotionReviewTime", 1); TEnv::IntVar StopMotionUseNumpad("StopMotionUseNumpad", 0); TEnv::IntVar StopMotionDrawBeneathLevels("StopMotionDrawBeneathLevels", 1); // Connected camera TEnv::StringVar StopMotionCameraName("CamCapCameraName", ""); // Camera resolution TEnv::StringVar StopMotionCameraResolution("CamCapCameraResolution", ""); namespace { //----------------------------------------------------------------------------- bool findCell(TXsheet *xsh, int col, const TXshCell &targetCell, int &bottomRowWithTheSameLevel) { bottomRowWithTheSameLevel = -1; TXshColumnP column = const_cast(xsh)->getColumn(col); if (!column) return false; TXshCellColumn *cellColumn = column->getCellColumn(); if (!cellColumn) return false; int r0, r1; if (!cellColumn->getRange(r0, r1)) return false; for (int r = r0; r <= r1; r++) { TXshCell cell = cellColumn->getCell(r); if (cell == targetCell) { bottomRowWithTheSameLevel = r; return true; } if (cell.m_level == targetCell.m_level) bottomRowWithTheSameLevel = r; } return false; } //----------------------------------------------------------------------------- QChar numToLetter(int letterNum) { switch (letterNum) { case 0: return QChar(); break; case 1: return 'A'; break; case 2: return 'B'; break; case 3: return 'C'; break; case 4: return 'D'; break; case 5: return 'E'; break; case 6: return 'F'; break; case 7: return 'G'; break; case 8: return 'H'; break; case 9: return 'I'; break; default: return QChar(); break; } } //----------------------------------------------------------------------------- QString convertToFrameWithLetter(int value, int length = -1) { QString str; str.setNum((int)(value / 10)); while (str.length() < length) str.push_front("0"); QChar letter = numToLetter(value % 10); if (!letter.isNull()) str.append(letter); return str; } //----------------------------------------------------------------------------- QString fidsToString(const std::vector &fids, bool letterOptionEnabled) { if (fids.empty()) return StopMotion::tr("No", "frame id"); QString retStr(""); if (letterOptionEnabled) { bool beginBlock = true; for (int f = 0; f < fids.size() - 1; f++) { int num = fids[f].getNumber(); int next_num = fids[f + 1].getNumber(); if (num % 10 == 0 && num + 10 == next_num) { if (beginBlock) { retStr += convertToFrameWithLetter(num) + " - "; beginBlock = false; } } else { retStr += convertToFrameWithLetter(num) + ", "; beginBlock = true; } } retStr += convertToFrameWithLetter(fids.back().getNumber()); } else { bool beginBlock = true; for (int f = 0; f < fids.size() - 1; f++) { int num = fids[f].getNumber(); int next_num = fids[f + 1].getNumber(); if (num + 1 == next_num) { if (beginBlock) { retStr += QString::number(num) + " - "; beginBlock = false; } } else { retStr += QString::number(num) + ", "; beginBlock = true; } } retStr += QString::number(fids.back().getNumber()); } return retStr; } //----------------------------------------------------------------------------- bool getRasterLevelSize(TXshLevel *level, TDimension &dim) { std::vector fids; level->getFids(fids); if (fids.empty()) return false; TXshSimpleLevel *simpleLevel = level->getSimpleLevel(); if (!simpleLevel) return false; TRasterImageP rimg = (TRasterImageP)simpleLevel->getFrame(fids[0], false); if (!rimg || rimg->isEmpty()) return false; dim = rimg->getRaster()->getSize(); return true; } }; // namespace //============================================================================= std::wstring FlexibleNameCreator::getPrevious() { if (m_s.empty() || (m_s[0] == 0 && m_s.size() == 1)) { m_s.push_back('Z' - 'A'); m_s.push_back('Z' - 'A'); return L"ZZ"; } int i = 0; int n = m_s.size(); while (i < n) { m_s[i]--; if (m_s[i] >= 0) break; m_s[i] = 'Z' - 'A'; i++; } if (i >= n) { n--; m_s.pop_back(); } std::wstring s; for (i = n - 1; i >= 0; i--) s.append(1, (wchar_t)(L'A' + m_s[i])); return s; } //------------------------------------------------------------------- bool FlexibleNameCreator::setCurrent(std::wstring name) { if (name.empty() || name.size() > 2) return false; std::vector newNameBuf; for (std::wstring::iterator it = name.begin(); it != name.end(); ++it) { int s = (int)((*it) - L'A'); if (s < 0 || s > 'Z' - 'A') return false; newNameBuf.push_back(s); } m_s.clear(); for (int i = newNameBuf.size() - 1; i >= 0; i--) m_s.push_back(newNameBuf[i]); return true; } //----------------------------------------------------------------------------- StopMotion::StopMotion() { m_opacity = StopMotionOpacity; m_webcam = new Webcam(); m_canon = Canon::instance(); m_serial = new StopMotionSerial(); m_light = new StopMotionLight(); m_alwaysLiveView = StopMotionAlwaysLiveView; m_placeOnXSheet = StopMotionPlaceOnXSheet; m_reviewTime = StopMotionReviewTime; m_useNumpadShortcuts = StopMotionUseNumpad; m_drawBeneathLevels = StopMotionDrawBeneathLevels; m_numpadForStyleSwitching = Preferences::instance()->isUseNumpadForSwitchingStylesEnabled(); setUseNumpadShortcuts(m_useNumpadShortcuts); m_turnOnRewind = Preferences::instance()->rewindAfterPlaybackEnabled(); m_timer = new QTimer(this); m_reviewTimer = new QTimer(this); m_reviewTimer->setSingleShot(true); m_intervalTimer = new QTimer(this); m_countdownTimer = new QTimer(this); m_webcamOverlayTimer = new QTimer(this); // Make the interval timer single-shot. When the capture finished, restart // timer for next frame. // This is because capturing and saving the image needs some time. m_intervalTimer->setSingleShot(true); m_webcamOverlayTimer->setSingleShot(true); TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet(); TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene(); TFrameHandle *frameHandle = TApp::instance()->getCurrentFrame(); bool ret = true; ret = ret && connect(xsheetHandle, SIGNAL(xsheetSwitched()), this, SLOT(update())); ret = ret && connect(m_reviewTimer, SIGNAL(timeout()), this, SLOT(onReviewTimeout())); ret = ret && connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(onSceneSwitched())); ret = ret && connect(frameHandle, SIGNAL(isPlayingStatusChanged()), this, SLOT(onPlaybackChanged())); ret = ret && connect(m_intervalTimer, SIGNAL(timeout()), this, SLOT(onIntervalCaptureTimerTimeout())); ret = ret && connect(m_webcamOverlayTimer, SIGNAL(timeout()), this, SLOT(captureWebcamOnTimeout())); ret = ret && connect(m_canon, SIGNAL(newCanonImageReady()), this, SLOT(directDslrImage())); assert(ret); ret = ret && connect(m_canon, SIGNAL(canonCameraChanged(QString)), this, SLOT(onCanonCameraChanged(QString))); assert(ret); ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); setToNextNewLevel(); // m_filePath = scene->getDefaultLevelPath(OVL_TYPE, // m_levelName.toStdWString()) // .getParentDir() // .getQString(); } //----------------------------------------------------------------- StopMotion::~StopMotion() { disconnectAllCameras(); #ifdef WITH_CANON m_canon->closeAll(); #endif } //----------------------------------------------------------------------------- void StopMotion::onSceneSwitched() { disconnectAllCameras(); TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); setToNextNewLevel(); m_filePath = scene->getDefaultLevelPath(OVL_TYPE, m_levelName.toStdWString()) .getParentDir() .getQString(); m_frameNumber = 1; m_liveViewImageMap.clear(); TLevelSet *levelSet = scene->getLevelSet(); std::vector levels; levelSet->listLevels(levels); int size = levels.size(); bool found = false; for (int i = 0; i < size; i++) { TXshLevel *level = levels[i]; if (level->getType() == OVL_XSHLEVEL) { TXshSimpleLevel *sl = 0; sl = level->getSimpleLevel(); bool isStopMotionLevel = sl->getProperties()->isStopMotionLevel(); if (isStopMotionLevel) { m_filePath = sl->getPath().getParentDir().getQString(); m_levelName = QString::fromStdWString(sl->getName()); m_frameNumber = sl->getFrameCount() + 1; setXSheetFrameNumber(xsh->getFrameCount() + 1); loadXmlFile(); buildLiveViewMap(sl); m_sl = sl; getRasterLevelSize(sl, m_canon->m_proxyImageDimensions); found = true; break; } } } if (!found) { setXSheetFrameNumber(1); } emit(levelNameChanged(m_levelName)); emit(filePathChanged(m_filePath)); emit(frameNumberChanged(m_frameNumber)); emit(xSheetFrameNumberChanged(m_xSheetFrameNumber)); refreshFrameInfo(); } //----------------------------------------------------------------- bool StopMotion::buildLiveViewMap(TXshSimpleLevel *sl) { m_liveViewImageMap.clear(); TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); std::wstring levelName = m_levelName.toStdWString(); if (levelName.empty()) { return false; } if (m_usingWebcam) { return false; } TFilePath liveViewFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_LiveView")); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); TFilePath actualLevelFp = scene->decodeFilePath(levelFp); TFilePath liveViewFp = scene->decodeFilePath(liveViewFolder + TFilePath(levelName + L"..jpg")); if (!TSystem::doesExistFileOrLevel(liveViewFolder)) return false; int count = sl->getFrameCount(); std::vector fids; sl->getFids(fids); for (TFrameId id : fids) { int frameNumber = id.getNumber(); if (TSystem::doesExistFileOrLevel(liveViewFp.withFrame(frameNumber))) { TRaster32P image; JpgConverter::loadJpg(liveViewFp.withFrame(frameNumber), image); m_liveViewImageMap.insert(std::pair(frameNumber, image)); } } if (m_liveViewImageMap.size() > 0) { return true; } else return false; } //----------------------------------------------------------------- void StopMotion::disconnectAllCameras() { #ifdef WITH_CANON if (m_liveViewStatus > LiveViewClosed) { m_canon->resetCanon(true); } else { m_canon->resetCanon(false); } #endif if (m_usingWebcam) { m_webcam->releaseWebcam(); m_usingWebcam = false; } m_webcam->clearWebcam(); m_liveViewStatus = LiveViewClosed; setTEnvCameraName(""); m_isTimeLapse = false; m_intervalStarted = false; m_intervalTimer->stop(); m_countdownTimer->stop(); m_liveViewImageDimensions = TDimension(0, 0); m_liveViewDpi = TPointD(0, 0); emit(intervalToggled(false)); emit(liveViewChanged(false)); emit(liveViewStopped()); emit(newCameraSelected(0, false)); toggleNumpadShortcuts(false); } //----------------------------------------------------------------- void StopMotion::onPlaybackChanged() { if (TApp::instance()->getCurrentFrame()->isPlaying() || m_liveViewStatus == LiveViewClosed) return; int r0, r1, step; ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); scene->getProperties()->getPreviewProperties()->getRange(r0, r1, step); if (r1 > -1) return; int frame = TApp::instance()->getCurrentFrame()->getFrame(); int lastFrame = TApp::instance()->getCurrentFrame()->getMaxFrameIndex(); if (m_xSheetFrameNumber - 1 == frame + 1) { TApp::instance()->getCurrentFrame()->setFrame(m_xSheetFrameNumber - 1); } } //----------------------------------------------------------------- void StopMotion::setOpacity(int opacity) { m_opacity = opacity; StopMotionOpacity = m_opacity; emit(opacityChanged(m_opacity)); } //----------------------------------------------------------------- void StopMotion::lowerOpacity() { int opacity = round((double)m_opacity / 255.0 * 10.0); opacity *= 10; opacity -= 10; m_opacity = double(opacity) / 100.0 * 255.0; m_opacity = std::max(0, m_opacity); StopMotionOpacity = m_opacity; emit(opacityChanged(m_opacity)); } //----------------------------------------------------------------- void StopMotion::raiseOpacity() { int opacity = round((double)m_opacity / 255.0 * 10.0); opacity *= 10; opacity += 10; m_opacity = double(opacity) / 100.0 * 255.0; m_opacity = std::min(255, m_opacity); StopMotionOpacity = m_opacity; emit(opacityChanged(m_opacity)); } //----------------------------------------------------------------- void StopMotion::setAlwaysLiveView(bool on) { m_alwaysLiveView = on; StopMotionAlwaysLiveView = int(on); emit(liveViewOnAllFramesSignal(on)); } //----------------------------------------------------------------- void StopMotion::setPlaceOnXSheet(bool on) { m_placeOnXSheet = on; StopMotionPlaceOnXSheet = int(on); emit(placeOnXSheetSignal(on)); } //----------------------------------------------------------------- void StopMotion::setReviewTime(int time) { m_reviewTime = time; StopMotionReviewTime = time; emit(reviewTimeChangedSignal(time)); } //----------------------------------------------------------------- void StopMotion::jumpToCameraFrame() { if (m_liveViewStatus == LiveViewPaused && !m_userCalledPause) { m_liveViewStatus = LiveViewOpen; } if (m_hasLineUpImage) m_showLineUpImage = true; TApp::instance()->getCurrentFrame()->setFrame(m_xSheetFrameNumber - 1); } //----------------------------------------------------------------- void StopMotion::removeStopMotionFrame() { if (m_xSheetFrameNumber == 1) return; TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); int row = m_xSheetFrameNumber - 2; // find which column the level is on. // check with the current column first int col = app->getCurrentColumn()->getColumnIndex(); TXshCell cell = xsh->getCell(row, col); TXshSimpleLevelP sl; bool found = false; if (!cell.isEmpty()) { if (cell.getSimpleLevel() != 0) { sl = cell.getSimpleLevel(); if (sl.getPointer()->getName() == m_levelName.toStdWString()) { found = true; } } } if (!found) { int cols = xsh->getColumnCount(); for (int i = 0; i < cols; i++) { cell = xsh->getCell(row, i); if (!cell.isEmpty()) { if (cell.getSimpleLevel() != 0) { sl = cell.getSimpleLevel(); if (sl.getPointer()->getName() == m_levelName.toStdWString()) { found = true; col = i; break; } } } } } if (!found) { // DVGui::error(tr("Could not find an xsheet level with the current // level")); return; } TXshCellColumn *xshCellColumn = xsh->getColumn(col)->getCellColumn(); if (!xshCellColumn) return; xshCellColumn->removeCells(row, 1); app->getCurrentScene()->getScene()->getXsheet()->updateFrameCount(); setXSheetFrameNumber(m_xSheetFrameNumber - 1); app->getCurrentFrame()->prevFrame(); app->getCurrentScene()->notifySceneChanged(); app->getCurrentXsheet()->notifyXsheetChanged(); } //----------------------------------------------------------------- void StopMotion::setUseNumpadShortcuts(bool on) { m_useNumpadShortcuts = on; StopMotionUseNumpad = int(on); emit(useNumpadSignal(on)); } //----------------------------------------------------------------- void StopMotion::setDrawBeneathLevels(bool on) { m_drawBeneathLevels = on; StopMotionDrawBeneathLevels = int(on); emit(drawBeneathLevelsSignal(on)); } //----------------------------------------------------------------- void StopMotion::toggleInterval(bool on) { m_isTimeLapse = on; emit(intervalToggled(on)); } //----------------------------------------------------------------- void StopMotion::startInterval() { if (m_liveViewStatus > 1) { m_intervalTimer->start(m_intervalTime * 1000); if (m_intervalTime != 0) m_countdownTimer->start(100); m_intervalStarted = true; emit(intervalStarted()); } else { DVGui::warning(tr("Please start live view before using time lapse.")); m_intervalStarted = false; emit(intervalStopped()); } } //----------------------------------------------------------------- void StopMotion::stopInterval() { m_intervalTimer->stop(); m_countdownTimer->stop(); m_intervalStarted = false; emit(intervalStopped()); } //----------------------------------------------------------------- void StopMotion::setIntervalAmount(int value) { m_intervalTime = value; emit(intervalAmountChanged(value)); } //----------------------------------------------------------------- void StopMotion::onIntervalCaptureTimerTimeout() { if (m_liveViewStatus > 0) { captureImage(); } else { DVGui::warning(tr("Please start live view before using time lapse.")); m_intervalStarted = false; emit(intervalStopped()); } } //----------------------------------------------------------------- void StopMotion::restartInterval() { // restart interval timer for capturing next frame (it is single shot) if (m_isTimeLapse && m_intervalStarted) { m_intervalTimer->start(m_intervalTime * 1000); // restart the count down as well (for aligning the timing. It is not // single shot) if (m_intervalTime != 0) m_countdownTimer->start(100); } } //----------------------------------------------------------------- void StopMotion::toggleNumpadShortcuts(bool on) { // can't just return if this feature is off // it could have been toggled while the camera was active if (!m_useNumpadShortcuts) on = false; CommandManager *comm = CommandManager::instance(); if (on) { m_oldActionMap.clear(); // if turning it on, get all old shortcuts if (m_numpadForStyleSwitching) { Preferences::instance()->setValue(useNumpadForSwitchingStyles, false); } std::string shortcut; QAction *action; for (int i = 0; i <= 9; i++) { shortcut = QString::number(i).toStdString(); action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } } shortcut = "+"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "-"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "Enter"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "Backspace"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "Return"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "*"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "."; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } shortcut = "/"; action = comm->getActionFromShortcut(shortcut); if (action) { m_oldActionMap.insert( std::pair(shortcut, action)); action->setShortcut(QKeySequence("")); action = NULL; } // now set all new shortcuts action = comm->getAction(MI_PrevFrame); if (action) { action->setShortcut(QKeySequence("1")); action = NULL; } action = comm->getAction(MI_StopMotionNextFrame); if (action) { action->setShortcut(QKeySequence("2")); action = NULL; } action = comm->getAction(MI_StopMotionJumpToCamera); if (action) { action->setShortcut(QKeySequence("3")); action = NULL; } action = comm->getAction(MI_Loop); if (action) { action->setShortcut(QKeySequence("8")); action = NULL; } action = comm->getAction(MI_Play); if (action) { action->setShortcut(QKeySequence("0")); action = NULL; } action = comm->getAction(MI_StopMotionRaiseOpacity); if (action) { action->setShortcut(QKeySequence("+")); action = NULL; } action = comm->getAction(MI_StopMotionLowerOpacity); if (action) { action->setShortcut(QKeySequence("-")); action = NULL; } action = comm->getAction(MI_StopMotionCapture); if (action) { action->setShortcut(QKeySequence("Return")); action = NULL; } action = comm->getAction(MI_StopMotionRemoveFrame); if (action) { action->setShortcut(QKeySequence("Backspace")); action = NULL; } action = comm->getAction(MI_StopMotionToggleLiveView); if (action) { action->setShortcut(QKeySequence("5")); action = NULL; } action = comm->getAction(MI_StopMotionToggleUseLiveViewImages); if (action) { action->setShortcut(QKeySequence(".")); action = NULL; } action = comm->getAction(MI_StopMotionToggleZoom); if (action) { action->setShortcut(QKeySequence("*")); action = NULL; } action = comm->getAction(MI_StopMotionPickFocusCheck); if (action) { action->setShortcut(QKeySequence("/")); action = NULL; } action = comm->getAction(MI_ShortPlay); if (action) { action->setShortcut(QKeySequence("6")); action = NULL; } } else { // unset the new shortcuts first if (m_oldActionMap.size() > 0) { QAction *action; action = comm->getAction(MI_PrevFrame); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionNextFrame); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionJumpToCamera); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_Loop); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_Play); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionCapture); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionLowerOpacity); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionRaiseOpacity); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionToggleLiveView); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionToggleUseLiveViewImages); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionToggleZoom); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_ShortPlay); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionPickFocusCheck); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } action = comm->getAction(MI_StopMotionRemoveFrame); if (action) { action->setShortcut( QKeySequence(comm->getShortcutFromAction(action).c_str())); action = NULL; } // now put back the old shortcuts auto it = m_oldActionMap.begin(); while (it != m_oldActionMap.end()) { it->second->setShortcut(QKeySequence(it->first.c_str())); it++; } if (m_numpadForStyleSwitching) { std::string shortcut; QAction *action; for (int i = 0; i <= 9; i++) { shortcut = QString::number(i).toStdString(); action = comm->getActionFromShortcut(shortcut); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } } Preferences::instance()->setValue(useNumpadForSwitchingStyles, true); } } } } //----------------------------------------------------------------- void StopMotion::toggleNumpadForFocusCheck(bool on) { CommandManager *comm = CommandManager::instance(); if (m_useNumpadShortcuts) { if (on) { QAction *action; action = comm->getAction(MI_PrevFrame); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_StopMotionNextFrame); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_StopMotionJumpToCamera); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_Loop); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_Play); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_StopMotionToggleLiveView); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_ShortPlay); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } action = comm->getAction(MI_StopMotionCapture); if (action) { action->setShortcut(QKeySequence("")); action = NULL; } } else { QAction *action; action = comm->getAction(MI_PrevFrame); if (action) { action->setShortcut(QKeySequence("1")); action = NULL; } action = comm->getAction(MI_StopMotionNextFrame); if (action) { action->setShortcut(QKeySequence("2")); action = NULL; } action = comm->getAction(MI_StopMotionJumpToCamera); if (action) { action->setShortcut(QKeySequence("3")); action = NULL; } action = comm->getAction(MI_Loop); if (action) { action->setShortcut(QKeySequence("8")); action = NULL; } action = comm->getAction(MI_Play); if (action) { action->setShortcut(QKeySequence("0")); action = NULL; } action = comm->getAction(MI_StopMotionToggleLiveView); if (action) { action->setShortcut(QKeySequence("5")); action = NULL; } action = comm->getAction(MI_ShortPlay); if (action) { action->setShortcut(QKeySequence("6")); action = NULL; } action = comm->getAction(MI_StopMotionCapture); if (action) { action->setShortcut(QKeySequence("Return")); action = NULL; } } } } //----------------------------------------------------------------- void StopMotion::setXSheetFrameNumber(int frameNumber) { m_xSheetFrameNumber = frameNumber; TApp::instance()->getCurrentFrame()->setFrame(frameNumber - 1); loadLineUpImage(); emit(xSheetFrameNumberChanged(m_xSheetFrameNumber)); m_serial->sendSerialData(); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } //----------------------------------------------------------------- void StopMotion::setCaptureNumberOfFrames(int frames) { m_captureNumberOfFrames = frames; emit(captureNumberOfFramesChanged(frames)); } //----------------------------------------------------------------- bool StopMotion::loadLineUpImage() { if (m_liveViewStatus == LiveViewClosed || m_userCalledPause) return false; int row; if (m_xSheetFrameNumber == 1) { row = 0; } else { row = m_xSheetFrameNumber - 2; } m_hasLineUpImage = loadLiveViewImage(row, m_lineUpImage); return m_hasLineUpImage; } //----------------------------------------------------------------- bool StopMotion::loadLiveViewImage(int row, TRaster32P &image) { // first see if the level exists in the current level set ToonzScene *currentScene = TApp::instance()->getCurrentScene()->getScene(); TLevelSet *levelSet = currentScene->getLevelSet(); std::wstring levelName = m_levelName.toStdWString(); // level with the same name TXshLevel *level_sameName = levelSet->getLevel(levelName); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); // level with the same path TXshLevel *level_samePath = levelSet->getLevel(*(currentScene), levelFp); // TFilePath actualLevelFp = currentScene->decodeFilePath(levelFp); TXshSimpleLevelP sl; if (level_sameName && level_samePath && level_sameName == level_samePath) { sl = dynamic_cast(level_sameName); bool isRasterLevel = sl && (sl->getType() == OVL_XSHLEVEL); if (!isRasterLevel) { return false; } } else return false; // next we need to find the column the level is on TApp *app = TApp::instance(); TXsheet *xsh = currentScene->getXsheet(); int col = app->getCurrentColumn()->getColumnIndex(); int foundCol = -1; // most possibly, it's in the current column int rowCheck; findCell(xsh, col, TXshCell(level_sameName, TFrameId(1)), rowCheck); if (rowCheck >= 0) { foundCol = col; } else { // search entire xsheet for (int c = 0; c < xsh->getColumnCount(); c++) { if (c == col) continue; findCell(xsh, c, TXshCell(level_sameName, TFrameId(1)), rowCheck); if (rowCheck >= 0) { foundCol = c; } } } if (rowCheck < 0) return false; // note found row represents the last row found that uses // the active level TXshCell cell = xsh->getCell(row, foundCol); if (!(cell.getSimpleLevel() != 0 && cell.getSimpleLevel() == level_sameName->getSimpleLevel())) { return false; } TFrameId frameId = xsh->getCell(row, foundCol).getFrameId(); int frameNumber = frameId.getNumber(); if (m_usingWebcam) { if (frameNumber > 0) { image = sl->getFrame(frameId, false)->raster(); return true; } else return false; } // first check if the image is in the live view map std::map::iterator it; it = m_liveViewImageMap.find(frameNumber); if (it != m_liveViewImageMap.end()) { image = m_liveViewImageMap.find(frameNumber)->second; return true; } // it's not in the map // now check to see if a file actually exists // then put it in the map TFilePath liveViewFolder = currentScene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_LiveView")); TFilePath liveViewFp = currentScene->decodeFilePath( liveViewFolder + TFilePath(levelName + L"..jpg")); TFilePath liveViewFile(liveViewFp.withFrame(frameNumber)); if (TFileStatus(liveViewFile).doesExist()) { if (JpgConverter::loadJpg(liveViewFile, image)) { m_liveViewImageMap.insert(std::pair(frameNumber, image)); return true; } } return false; } //----------------------------------------------------------------- void StopMotion::setFrameNumber(int frameNumber) { m_frameNumber = frameNumber; emit(frameNumberChanged(m_frameNumber)); } //----------------------------------------------------------------------------- void StopMotion::nextFrame() { if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) { int f = m_frameNumber; if (f % 10 == 0) // next number m_frameNumber = ((int)(f / 10) + 1) * 10; else // next alphabet m_frameNumber = f + 1; } else m_frameNumber = m_frameNumber + 1; emit(frameNumberChanged(m_frameNumber)); refreshFrameInfo(); } //----------------------------------------------------------------------------- void StopMotion::lastFrame() {} //----------------------------------------------------------------------------- void StopMotion::previousFrame() { int f = m_frameNumber; if (f > 1) { if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) { if (f % 10 == 0) // next number m_frameNumber = ((int)(f / 10) - 1) * 10; else // next alphabet m_frameNumber = f - 1; } else m_frameNumber = f - 1; emit(frameNumberChanged(m_frameNumber)); refreshFrameInfo(); } } //----------------------------------------------------------------- void StopMotion::setLevelName(QString levelName) { m_levelName = levelName; } //----------------------------------------------------------------- void StopMotion::nextName() { std::unique_ptr nameCreator(new FlexibleNameCreator()); if (!nameCreator->setCurrent(m_levelName.toStdWString())) { setToNextNewLevel(); return; } std::wstring levelName = nameCreator->getNext(); updateLevelNameAndFrame(levelName); } //----------------------------------------------------------------- void StopMotion::previousName() { std::unique_ptr nameCreator(new FlexibleNameCreator()); std::wstring levelName; // if the current level name is non-sequential, then try to switch the last // sequential level in the scene. if (!nameCreator->setCurrent(m_levelName.toStdWString())) { TLevelSet *levelSet = TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); nameCreator->setCurrent(L"ZZ"); for (;;) { levelName = nameCreator->getPrevious(); if (levelSet->getLevel(levelName) != 0) break; if (levelName == L"A") { setToNextNewLevel(); return; } } } else levelName = nameCreator->getPrevious(); updateLevelNameAndFrame(levelName); } //----------------------------------------------------------------- void StopMotion::setFileType(QString fileType) { m_fileType = fileType; emit(fileTypeChanged(m_fileType)); } //----------------------------------------------------------------- void StopMotion::setFilePath(QString filePath) { m_filePath = filePath; ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); TFilePath saveInPath(filePath.toStdWString()); scene->getProperties()->setCameraCaptureSaveInPath(saveInPath); refreshFrameInfo(); emit(filePathChanged(m_filePath)); } //----------------------------------------------------------------- void StopMotion::setSubsamplingValue(int subsampling) { m_subsampling = subsampling; } //----------------------------------------------------------------- void StopMotion::getSubsampling() { ToonzScene *currentScene = TApp::instance()->getCurrentScene()->getScene(); TLevelSet *levelSet = currentScene->getLevelSet(); std::wstring levelName = m_levelName.toStdWString(); // level with the same name TXshLevel *level_sameName = levelSet->getLevel(levelName); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); // level with the same path TXshLevel *level_samePath = levelSet->getLevel(*(currentScene), levelFp); TFilePath actualLevelFp = currentScene->decodeFilePath(levelFp); if (level_sameName && level_samePath && level_sameName == level_samePath) { TXshSimpleLevelP m_sl; m_sl = dynamic_cast(level_sameName); bool isRasterLevel = m_sl && (m_sl->getType() == OVL_XSHLEVEL); if (isRasterLevel) { int currSubsampling = m_sl->getProperties()->getSubsampling(); m_subsampling = currSubsampling; emit(subsamplingChanged(m_subsampling)); } else emit(subsamplingChanged(-1)); } else emit(subsamplingChanged(-1)); } //----------------------------------------------------------------------------- void StopMotion::update() { getSubsampling(); } //----------------------------------------------------------------------------- void StopMotion::setSubsampling() { ToonzScene *currentScene = TApp::instance()->getCurrentScene()->getScene(); TLevelSet *levelSet = currentScene->getLevelSet(); std::wstring levelName = m_levelName.toStdWString(); // level with the same name TXshLevel *level_sameName = levelSet->getLevel(levelName); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); // level with the same path TXshLevel *level_samePath = levelSet->getLevel(*(currentScene), levelFp); TFilePath actualLevelFp = currentScene->decodeFilePath(levelFp); if (level_sameName && level_samePath && level_sameName == level_samePath) { TXshSimpleLevelP m_sl; m_sl = dynamic_cast(level_sameName); bool isRasterLevel = m_sl && (m_sl->getType() & RASTER_TYPE); if (isRasterLevel) { int currSubsampling = m_sl->getProperties()->getSubsampling(); int newSubsampling = m_subsampling; if (currSubsampling != newSubsampling) { m_sl->getProperties()->setSubsampling(newSubsampling); m_sl->invalidateFrames(); TApp::instance()->getCurrentScene()->setDirtyFlag(true); TApp::instance() ->getCurrentXsheet() ->getXsheet() ->getStageObjectTree() ->invalidateAll(); TApp::instance()->getCurrentLevel()->notifyLevelChange(); emit(subsamplingChanged(m_subsampling)); } } } } //----------------------------------------------------------------------------- void StopMotion::onTimeout() { int currentFrame = TApp::instance()->getCurrentFrame()->getFrame(); if ((m_liveViewStatus > LiveViewClosed && m_liveViewStatus < LiveViewPaused && !TApp::instance()->getCurrentFrame()->isPlaying()) || (m_liveViewStatus == LiveViewPaused && !m_userCalledPause)) { if (getAlwaysLiveView() || (currentFrame >= m_xSheetFrameNumber - 2)) { if (!m_usingWebcam) { #ifdef WITH_CANON bool success = m_canon->downloadEVFData(); if (success) { setLiveViewImage(); } else { m_hasLiveViewImage = false; } #endif } else { bool success = m_webcam->getWebcamImage(m_liveViewImage); if (success) { setLiveViewImage(); } else { m_hasLiveViewImage = false; } } if ((!getAlwaysLiveView() && !(currentFrame >= m_xSheetFrameNumber - 2)) || m_canon->m_pickLiveViewZoom) { m_showLineUpImage = false; } else { m_showLineUpImage = true; } } else if (m_liveViewStatus == LiveViewOpen) { m_liveViewStatus = LiveViewPaused; TApp::instance()->getCurrentScene()->notifySceneChanged(); } } } //----------------------------------------------------------------------------- void StopMotion::setLiveViewImage() { m_hasLiveViewImage = true; // make sure not to set to LiveViewOpen if it has been turned off if (m_liveViewStatus > LiveViewClosed && !m_userCalledPause) { m_liveViewStatus = LiveViewOpen; } if (m_liveViewDpi == TPointD(0.0, 0.0) || m_liveViewImageDimensions.lx == 0) { m_liveViewImageDimensions = m_liveViewImage->getSize(); if (m_usingWebcam) { m_liveViewDpi = TPointD(Stage::standardDpi, Stage::standardDpi); } else { double minimumDpi; if (m_canon->m_proxyImageDimensions == TDimension(0, 0)) { TCamera *camera = TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera(); TDimensionD size = camera->getSize(); minimumDpi = std::min(m_liveViewImageDimensions.lx / size.lx, m_liveViewImageDimensions.ly / size.ly); } else { TDimensionD size = TDimensionD( (double)m_canon->m_proxyImageDimensions.lx / Stage::standardDpi, (double)m_canon->m_proxyImageDimensions.ly / Stage::standardDpi); minimumDpi = std::min(m_liveViewImageDimensions.lx / size.lx, m_liveViewImageDimensions.ly / size.ly); } m_liveViewDpi = TPointD(minimumDpi, minimumDpi); } emit(newDimensions()); } emit(newLiveViewImageReady()); } //----------------------------------------------------------------------------- void StopMotion::onReviewTimeout() { if (m_liveViewStatus > LiveViewClosed) { m_liveViewStatus = LiveViewOpen; m_timer->start(40); } TApp::instance()->getCurrentFrame()->setFrame(m_xSheetFrameNumber - 1); } //----------------------------------------------------------------------------- bool StopMotion::importImage() { TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); TPointD dpi = TPointD(Stage::standardDpi, Stage::standardDpi); std::wstring levelName = m_levelName.toStdWString(); if (levelName.empty()) { DVGui::error( tr("No level name specified: please choose a valid level name")); return false; } if (m_usingWebcam) { m_newImage = m_liveViewImage; } m_light->hideOverlays(); int frameNumber = m_frameNumber; /* create parent directory if it does not exist */ TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath fullResFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_FullRes")); TFilePath liveViewFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_LiveView")); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); TFilePath actualLevelFp = scene->decodeFilePath(levelFp); TFilePath actualFile(actualLevelFp.withFrame(frameNumber)); TFilePath fullResFp = scene->decodeFilePath(fullResFolder + TFilePath(levelName + L"..jpg")); TFilePath fullResFile(fullResFp.withFrame(frameNumber)); TFilePath fullResRawFile(fullResFile.getQString().replace( fullResFile.getQString().lastIndexOf("jpg"), 3, "cr2")); m_tempRaw = fullResRawFile.getQString(); TFilePath liveViewFp = scene->decodeFilePath(liveViewFolder + TFilePath(levelName + L"..jpg")); TFilePath liveViewFile(liveViewFp.withFrame(frameNumber)); TFilePath tempFile = parentDir + "temp.jpg"; TFilePath tempRaw = parentDir + "temp.cr2"; TXshSimpleLevel *sl = 0; TXshLevel *level = scene->getLevelSet()->getLevel(levelName); enum State { NEWLEVEL = 0, ADDFRAME, OVERWRITE } state; /* if the level already exists in the scene cast */ if (level) { /* if the existing level is not a raster level, then return */ if (level->getType() != OVL_XSHLEVEL) { DVGui::error( tr("The level name specified is already used: please choose a " "different level name.")); return false; } /* if the existing level does not match file path and pixel size, then * return */ sl = level->getSimpleLevel(); if (scene->decodeFilePath(sl->getPath()) != actualLevelFp) { DVGui::error( tr("The save in path specified does not match with the existing " "level.")); return false; } if (sl->getProperties()->getImageRes() != TDimension(m_newImage->getLx(), m_newImage->getLy())) { DVGui::error(tr( "The captured image size does not match with the existing level.")); return false; } /* if the level already have the same frame, then ask if overwrite it */ TFilePath frameFp(actualLevelFp.withFrame(frameNumber)); if (TFileStatus(frameFp).doesExist()) { QString question = tr("File %1 already exists.\nDo you want to overwrite it?") .arg(toQString(frameFp)); int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"), QObject::tr("Cancel")); if (ret == 0 || ret == 2) return false; state = OVERWRITE; } else state = ADDFRAME; } /* if the level does not exist in the scene cast */ else { /* if the file does exist, load it first */ if (TSystem::doesExistFileOrLevel(actualLevelFp)) { level = scene->loadLevel(actualLevelFp); if (!level) { DVGui::error(tr("Failed to load %1.").arg(toQString(actualLevelFp))); return false; } /* if the loaded level does not match in pixel size, then return */ sl = level->getSimpleLevel(); if (!sl || sl->getProperties()->getImageRes() != TDimension(m_newImage->getLx(), m_newImage->getLy())) { DVGui::error( tr("The captured image size does not match with the existing " "level.")); return false; } /* confirm overwrite */ TFilePath frameFp(actualLevelFp.withFrame(frameNumber)); if (TFileStatus(frameFp).doesExist()) { QString question = tr("File %1 already exists.\nDo you want to overwrite it?") .arg(toQString(frameFp)); int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"), QObject::tr("Cancel")); if (ret == 0 || ret == 2) return false; } } /* if the file does not exist, then create a new level */ else { TXshLevel *level = scene->createNewLevel(OVL_XSHLEVEL, levelName, TDimension(), 0, levelFp); sl = level->getSimpleLevel(); sl->setPath(levelFp, true); sl->getProperties()->setDpiPolicy(LevelProperties::DP_CustomDpi); sl->getProperties()->setDpi(Stage::standardDpi); sl->getProperties()->setImageDpi(dpi); sl->getProperties()->setImageRes( TDimension(m_newImage->getLx(), m_newImage->getLy())); } state = NEWLEVEL; getSubsampling(); } if (!TFileStatus(parentDir).doesExist()) { QString question; question = tr("Folder %1 doesn't exist.\nDo you want to create it?") .arg(toQString(parentDir)); int ret = DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No")); if (ret == 0 || ret == 2) return false; try { TSystem::mkDir(parentDir); DvDirModel::instance()->refreshFolder(parentDir.getParentDir()); } catch (...) { DVGui::error(tr("Unable to create") + toQString(parentDir)); return false; } } if (!m_usingWebcam) { if (!TFileStatus(fullResFolder).doesExist()) { try { TSystem::mkDir(fullResFolder); DvDirModel::instance()->refreshFolder(fullResFolder.getParentDir()); } catch (...) { DVGui::error(tr("Unable to create") + toQString(fullResFolder)); return false; } } if (!TFileStatus(liveViewFolder).doesExist()) { try { TSystem::mkDir(liveViewFolder); DvDirModel::instance()->refreshFolder(liveViewFolder.getParentDir()); } catch (...) { DVGui::error(tr("Unable to create") + toQString(liveViewFolder)); return false; } } } // move the temp file if (!m_usingWebcam) { TSystem::copyFile(fullResFile, tempFile); TSystem::deleteFile(tempFile); if (TSystem::doesExistFileOrLevel(TFilePath(tempRaw))) { TSystem::copyFile(fullResRawFile, tempRaw); TSystem::deleteFile(tempRaw); } if (m_hasLineUpImage) { JpgConverter::saveJpg(m_lineUpImage, liveViewFile); // check the live view image map to see if there is already // an image with this framenumber. Overwrite if it exists std::map::iterator it; it = m_liveViewImageMap.find(frameNumber); if (it != m_liveViewImageMap.end()) { m_liveViewImageMap.find(frameNumber)->second = m_lineUpImage; return true; } else { m_liveViewImageMap.insert( std::pair(m_frameNumber, m_lineUpImage)); } } } TFrameId fid(frameNumber); /* create the raster */ TRaster32P raster = m_newImage; TRasterImageP ri(raster); ri->setDpi(dpi.x, dpi.x); /* setting the frame */ sl->setFrame(fid, ri); /* set dirty flag */ sl->getProperties()->setDirtyFlag(true); sl->getProperties()->setIsStopMotion(true); sl->setIsReadOnly(true); // if (m_saveOnCaptureCB->isChecked()) sl->save(); // for now always save. This can be tweaked later sl->save(); m_sl = sl; if (getReviewTime() > 0 && !m_isTimeLapse) { m_liveViewStatus = LiveViewPaused; m_reviewTimer->start(getReviewTime() * 1000); } /* placement in xsheet */ if (!getPlaceOnXSheet()) { postImportProcess(); return true; } int row = m_xSheetFrameNumber - 1; int col = std::max(0, app->getCurrentColumn()->getColumnIndex()); // if the level is newly created or imported, then insert a new column if (state == NEWLEVEL) { if (!xsh->isColumnEmpty(col)) { col += 1; xsh->insertColumn(col); } for (int i = 0; i < m_captureNumberOfFrames; i++) { xsh->insertCells(row + i, col); xsh->setCell(row + i, col, TXshCell(sl, fid)); } app->getCurrentColumn()->setColumnIndex(col); if (getReviewTime() == 0 || m_isTimeLapse) app->getCurrentFrame()->setFrame(row + m_captureNumberOfFrames); m_xSheetFrameNumber = row + 1 + m_captureNumberOfFrames; emit(xSheetFrameNumberChanged(m_xSheetFrameNumber)); postImportProcess(); return true; } // state == OVERWRITE, ADDFRAME // if the same cell is already in the column, then just replace the content // and do not set a new cell int foundCol, foundRow = -1; // most possibly, it's in the current column int rowCheck; if (findCell(xsh, col, TXshCell(sl, fid), rowCheck)) { postImportProcess(); return true; } if (rowCheck >= 0) { foundRow = rowCheck; foundCol = col; } // search entire xsheet for (int c = 0; c < xsh->getColumnCount(); c++) { if (c == col) continue; if (findCell(xsh, c, TXshCell(sl, fid), rowCheck)) { postImportProcess(); return true; } if (rowCheck >= 0) { foundRow = rowCheck; foundCol = c; } } // note found row represents the last row found that uses // the active level // if there is a column containing the same level if (foundRow >= 0) { for (int i = 0; i < m_captureNumberOfFrames; i++) { xsh->insertCells(row + i, foundCol); xsh->setCell(row + i, foundCol, TXshCell(sl, fid)); } app->getCurrentColumn()->setColumnIndex(foundCol); if (getReviewTime() == 0 || m_isTimeLapse) app->getCurrentFrame()->setFrame(row + m_captureNumberOfFrames); m_xSheetFrameNumber = row + 1 + m_captureNumberOfFrames; emit(xSheetFrameNumberChanged(m_xSheetFrameNumber)); } // if the level is registered in the scene, but is not placed in the xsheet, // then insert a new column else { if (!xsh->isColumnEmpty(col)) { col += 1; xsh->insertColumn(col); } for (int i = 0; i < m_captureNumberOfFrames; i++) { xsh->setCell(row + i, col, TXshCell(sl, fid)); } app->getCurrentColumn()->setColumnIndex(col); if (getReviewTime() == 0 || m_isTimeLapse) app->getCurrentFrame()->setFrame(row + m_captureNumberOfFrames); m_xSheetFrameNumber = row + 1 + m_captureNumberOfFrames; emit(xSheetFrameNumberChanged(m_xSheetFrameNumber)); } postImportProcess(); return true; } //----------------------------------------------------------------- void StopMotion::captureImage() { if (m_isTimeLapse && !m_intervalStarted) { startInterval(); return; } if (m_isTimeLapse && m_intervalStarted && m_intervalTimer->isActive()) { stopInterval(); return; } if (!m_hasLiveViewImage || m_liveViewStatus != LiveViewOpen) { DVGui::warning(tr("Cannot capture image unless live view is active.")); return; } bool sessionOpen = false; #ifdef WITH_CANON sessionOpen = m_canon->m_sessionOpen; #endif if ((!m_usingWebcam && !sessionOpen) || m_userCalledPause) { DVGui::warning(tr("Please start live view before capturing an image.")); return; } if (m_usingWebcam) { captureWebcamImage(); } else { captureDslrImage(); } } //----------------------------------------------------------------------------- void StopMotion::captureWebcamImage() { if (m_light->useOverlays()) { m_light->showOverlays(); m_webcamOverlayTimer->start(500); } else { if (getReviewTime() > 0 && !m_isTimeLapse) { m_timer->stop(); if (m_liveViewStatus > LiveViewClosed) { // m_liveViewStatus = LiveViewPaused; } } m_lineUpImage = m_liveViewImage; m_hasLineUpImage = true; emit(newLiveViewImageReady()); importImage(); return; } } //----------------------------------------------------------------------------- void StopMotion::captureWebcamOnTimeout() { if (m_isTestShot) { saveTestShot(); return; } if (getReviewTime() > 0 && !m_isTimeLapse) { m_timer->stop(); if (m_liveViewStatus > LiveViewClosed) { // m_liveViewStatus = LiveViewPaused; } } m_lineUpImage = m_liveViewImage; m_hasLineUpImage = true; emit(newLiveViewImageReady()); importImage(); return; } //----------------------------------------------------------------------------- void StopMotion::captureDslrImage() { m_light->showOverlays(); #ifdef WITH_CANON if (m_canon->m_zooming) { DVGui::warning( tr("Can't capture an image with focus check on.\n" "Please click the Check button in the Settings tab.")); return; } #endif if (getReviewTime() > 0 && !m_isTimeLapse) { m_timer->stop(); } if (m_liveViewStatus > LiveViewClosed && !m_isTimeLapse) { // m_liveViewStatus = LiveViewPaused; } if (m_hasLiveViewImage) { m_lineUpImage = m_liveViewImage; m_hasLineUpImage = true; emit(newLiveViewImageReady()); } TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); int frameNumber = m_frameNumber; std::wstring levelName = m_levelName.toStdWString(); TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath tempFile = parentDir + "temp.jpg"; TFilePath tempRaw = parentDir + "temp.cr2"; if (!TFileStatus(parentDir).doesExist()) { TSystem::mkDir(parentDir); } m_tempFile = tempFile.getQString(); m_tempRaw = tempRaw.getQString(); #ifdef WITH_CANON m_canon->takePicture(); #endif } //----------------------------------------------------------------------------- void StopMotion::directDslrImage() { if (m_isTestShot) saveTestShot(); else importImage(); #ifdef WITH_CANON if (m_canon->m_liveViewExposureOffset != 0) { m_canon->setShutterSpeed(m_canon->m_realShutterSpeed, false); } #endif } //----------------------------------------------------------------------------- void StopMotion::postImportProcess() { saveXmlFile(); if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) { int f = m_frameNumber; if (f % 10 == 0) // next number m_frameNumber = ((int)(f / 10) + 1) * 10; else // next alphabet m_frameNumber = f + 1; } else m_frameNumber += 1; emit(frameNumberChanged(m_frameNumber)); /* notify */ refreshFrameInfo(); if (m_isTimeLapse && m_intervalStarted) restartInterval(); TApp::instance()->getCurrentScene()->notifySceneChanged(); TApp::instance()->getCurrentScene()->notifyCastChange(); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } //----------------------------------------------------------------- void StopMotion::takeTestShot() { bool sessionOpen = false; #ifdef WITH_CANON sessionOpen = m_canon->m_sessionOpen; #endif if ((!m_usingWebcam && !sessionOpen) || m_userCalledPause) { DVGui::warning(tr("Please start live view before capturing an image.")); return; } m_isTestShot = true; if (m_usingWebcam) { if (m_light->useOverlays()) { m_light->showOverlays(); m_webcamOverlayTimer->start(500); } else { saveTestShot(); } } #ifdef WITH_CANON else { TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath tempFile = parentDir + "temp.jpg"; TFilePath tempRaw = parentDir + "temp.cr2"; if (!TFileStatus(parentDir).doesExist()) { TSystem::mkDir(parentDir); } m_tempFile = tempFile.getQString(); m_tempRaw = tempRaw.getQString(); m_light->showOverlays(); m_canon->takePicture(); } #endif } //----------------------------------------------------------------------------- void StopMotion::saveTestShot() { m_isTestShot = false; TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); std::wstring levelName = m_levelName.toStdWString(); if (levelName.empty()) { DVGui::error( tr("No level name specified: please choose a valid level name")); return; } if (m_usingWebcam) { m_newImage = m_liveViewImage; } m_light->hideOverlays(); /* create parent directory if it does not exist */ TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath testsFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_Tests")); TFilePath testsThumbsFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_Tests") + TFilePath("Thumbs")); if (!TFileStatus(parentDir).doesExist()) { QString question; question = tr("Folder %1 doesn't exist.\nDo you want to create it?") .arg(toQString(parentDir)); int ret = DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No")); if (ret == 0 || ret == 2) return; try { TSystem::mkDir(parentDir); DvDirModel::instance()->refreshFolder(parentDir.getParentDir()); } catch (...) { DVGui::error(tr("Unable to create") + toQString(parentDir)); return; } } if (!TFileStatus(testsFolder).doesExist()) { try { TSystem::mkDir(testsFolder); DvDirModel::instance()->refreshFolder(testsFolder.getParentDir()); } catch (...) { DVGui::error(tr("Unable to create") + toQString(testsFolder)); return; } } if (!TFileStatus(testsThumbsFolder).doesExist()) { try { TSystem::mkDir(testsThumbsFolder); DvDirModel::instance()->refreshFolder(testsThumbsFolder.getParentDir()); } catch (...) { DVGui::error(tr("Unable to create") + toQString(testsThumbsFolder)); return; } } TFilePathSet set = TSystem::readDirectory(testsFolder, false, true, false); int fileNumber = 0; for (auto const &tempPath : set) { if (tempPath.getUndottedType() == "jpg") { fileNumber++; } } TFilePath testsFile = scene->decodeFilePath( testsFolder + TFilePath(levelName + L"+" + QString::number(fileNumber).toStdWString() + L".jpg")); TFilePath testsXml = scene->decodeFilePath(testsFolder + TFilePath(levelName + L"_tests.xml")); TFilePath testsThumbsFile = scene->decodeFilePath( testsThumbsFolder + TFilePath(levelName + L"+" + QString::number(fileNumber).toStdWString() + L".jpg")); TFilePath fullResRawFile(testsThumbsFile.getQString().replace( testsThumbsFile.getQString().lastIndexOf("jpg"), 3, "cr2")); m_tempRaw = fullResRawFile.getQString(); TFilePath tempFile = parentDir + "temp.jpg"; TFilePath tempRaw = parentDir + "temp.cr2"; if (!m_usingWebcam) { TSystem::copyFile(testsFile, tempFile); TSystem::deleteFile(tempFile); if (TSystem::doesExistFileOrLevel(TFilePath(tempRaw))) { TSystem::copyFile(fullResRawFile, tempRaw); TSystem::deleteFile(tempRaw); } } else { JpgConverter::saveJpg(m_newImage, testsFile); } cv::Mat imgOriginal(m_newImage->getSize().ly, m_newImage->getSize().lx, CV_8UC4); int size = m_newImage->getPixelSize() * m_newImage->getSize().lx * m_newImage->getSize().ly; uchar *imgBuf = imgOriginal.data; m_newImage->lock(); uchar *rawData = m_newImage->getRawData(); memcpy(imgBuf, rawData, size); m_newImage->unlock(); int w = m_newImage->getSize().lx; double r = (double)m_newImage->getSize().lx / (double)m_newImage->getSize().ly; cv::Size dim(120, 120.0 / r); cv::Mat imgThumb(dim, CV_8UC4); cv::resize(imgOriginal, imgThumb, dim); cv::flip(imgThumb, imgThumb, 0); cv::imwrite(testsThumbsFile.getQString().toStdString(), imgThumb); saveTestXml(testsXml, fileNumber); emit(updateTestShots()); FlipBook *fb = ::viewFile(testsFile); } //----------------------------------------------------------------------------- void StopMotion::saveTestXml(TFilePath testsXml, int number) { QString xmlFileName = testsXml.getQString(); if (!TSystem::doesExistFileOrLevel(testsXml)) { QFile xmlFile(xmlFileName); xmlFile.open(QIODevice::WriteOnly); QXmlStreamWriter xmlWriter(&xmlFile); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument(); xmlWriter.writeStartElement("body"); xmlWriter.writeStartElement("Test_" + QString::number(number)); if (m_usingWebcam) { xmlWriter.writeTextElement("Webcam", "yes"); xmlWriter.writeTextElement("CameraName", m_webcam->getWebcamDescription()); xmlWriter.writeTextElement("CameraResolutionX", QString::number(m_webcam->getWebcamWidth())); xmlWriter.writeTextElement("CameraResolutionY", QString::number(m_webcam->getWebcamHeight())); } else { xmlWriter.writeTextElement("Webcam", "no"); #ifdef WITH_CANON xmlWriter.writeTextElement("CameraName", QString::fromStdString(m_canon->m_cameraName)); xmlWriter.writeTextElement("Aperture", m_canon->getCurrentAperture()); xmlWriter.writeTextElement("ShutterSpeed", m_canon->m_displayedShutterSpeed); xmlWriter.writeTextElement("ISO", m_canon->getCurrentIso()); xmlWriter.writeTextElement("PictureStyle", m_canon->getCurrentPictureStyle()); xmlWriter.writeTextElement("ImageQuality", m_canon->getCurrentImageQuality()); xmlWriter.writeTextElement("WhiteBalance", m_canon->getCurrentWhiteBalance()); xmlWriter.writeTextElement("ColorTemperature", m_canon->getCurrentColorTemperature()); xmlWriter.writeTextElement("ExposureCompensation", m_canon->getCurrentExposureCompensation()); #endif } xmlWriter.writeEndElement(); xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); xmlFile.close(); } else { QFile file(xmlFileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << "Failed to open file"; return; } QDomDocument document; if (!document.setContent(&file)) { qDebug() << "failed to parse file"; file.close(); return; } file.close(); QDomElement docEle = document.documentElement(); QDomElement newTest = document.createElement("Test_" + QString::number(number)); if (m_usingWebcam) { QDomElement webcam = document.createElement("Webcam"); newTest.appendChild(webcam); webcam.appendChild(document.createTextNode("yes")); QDomElement cameraName = document.createElement("CameraName"); newTest.appendChild(cameraName); cameraName.appendChild( document.createTextNode(m_webcam->getWebcamDescription())); QDomElement resolutionX = document.createElement("CameraResolutionX"); newTest.appendChild(resolutionX); resolutionX.appendChild( document.createTextNode(QString::number(m_webcam->getWebcamWidth()))); QDomElement resolutionY = document.createElement("CameraResolutionY"); newTest.appendChild(resolutionY); resolutionY.appendChild(document.createTextNode( QString::number(m_webcam->getWebcamHeight()))); } else { QDomElement webcam = document.createElement("Webcam"); newTest.appendChild(webcam); webcam.appendChild(document.createTextNode("no")); #ifdef WITH_CANON QDomElement cameraName = document.createElement("CameraName"); newTest.appendChild(cameraName); cameraName.appendChild(document.createTextNode( QString::fromStdString(m_canon->m_cameraName))); QDomElement aperture = document.createElement("Aperture"); newTest.appendChild(aperture); aperture.appendChild( document.createTextNode(m_canon->getCurrentAperture())); QDomElement shutterSpeed = document.createElement("ShutterSpeed"); newTest.appendChild(shutterSpeed); shutterSpeed.appendChild( document.createTextNode(m_canon->m_displayedShutterSpeed)); QDomElement iso = document.createElement("ISO"); newTest.appendChild(iso); iso.appendChild(document.createTextNode(m_canon->getCurrentIso())); QDomElement pictureStyle = document.createElement("PictureStyle"); newTest.appendChild(pictureStyle); pictureStyle.appendChild( document.createTextNode(m_canon->getCurrentPictureStyle())); QDomElement imageQuality = document.createElement("ImageQuality"); newTest.appendChild(imageQuality); imageQuality.appendChild( document.createTextNode(m_canon->getCurrentImageQuality())); QDomElement whiteBalance = document.createElement("WhiteBalance"); newTest.appendChild(whiteBalance); whiteBalance.appendChild( document.createTextNode(m_canon->getCurrentWhiteBalance())); QDomElement colorTemperature = document.createElement("ColorTemperature"); newTest.appendChild(colorTemperature); colorTemperature.appendChild( document.createTextNode(m_canon->getCurrentColorTemperature())); QDomElement exposureCompensation = document.createElement("ExposureCompensation"); newTest.appendChild(exposureCompensation); exposureCompensation.appendChild( document.createTextNode(m_canon->getCurrentExposureCompensation())); #endif } docEle.appendChild(newTest); file.open(QIODevice::WriteOnly); file.resize(0); QTextStream stream; stream.setDevice(&file); document.save(stream, 4); file.close(); } } //----------------------------------------------------------------------------- void StopMotion::saveXmlFile() { TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); std::wstring levelName = m_levelName.toStdWString(); TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath tempFile = parentDir + TFilePath(levelName + L".xml"); QString xmlFileName = tempFile.getQString(); QFile xmlFile(xmlFileName); xmlFile.open(QIODevice::WriteOnly); QXmlStreamWriter xmlWriter(&xmlFile); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument(); xmlWriter.writeStartElement("body"); xmlWriter.writeStartElement("SceneInfo"); xmlWriter.writeTextElement("LevelName", QString::fromStdWString(levelName)); xmlWriter.writeTextElement("CurrentFrame", QString::number(m_xSheetFrameNumber)); xmlWriter.writeEndElement(); xmlWriter.writeStartElement("CameraInfo"); if (m_usingWebcam) { xmlWriter.writeTextElement("Webcam", "yes"); xmlWriter.writeTextElement("CameraName", m_webcam->getWebcamDescription()); xmlWriter.writeTextElement("CameraResolutionX", QString::number(m_webcam->getWebcamWidth())); xmlWriter.writeTextElement("CameraResolutionY", QString::number(m_webcam->getWebcamHeight())); } else { xmlWriter.writeTextElement("Webcam", "no"); #ifdef WITH_CANON xmlWriter.writeTextElement("CameraName", QString::fromStdString(m_canon->m_cameraName)); xmlWriter.writeTextElement("Aperture", m_canon->getCurrentAperture()); xmlWriter.writeTextElement("ShutterSpeed", m_canon->m_displayedShutterSpeed); xmlWriter.writeTextElement("ISO", m_canon->getCurrentIso()); xmlWriter.writeTextElement("PictureStyle", m_canon->getCurrentPictureStyle()); xmlWriter.writeTextElement("ImageQuality", m_canon->getCurrentImageQuality()); xmlWriter.writeTextElement("WhiteBalance", m_canon->getCurrentWhiteBalance()); xmlWriter.writeTextElement("ColorTemperature", m_canon->getCurrentColorTemperature()); xmlWriter.writeTextElement("ExposureCompensation", m_canon->getCurrentExposureCompensation()); xmlWriter.writeTextElement("FocusCheckLocationX", QString::number(m_canon->m_finalZoomPoint.x)); xmlWriter.writeTextElement("FocusCheckLocationY", QString::number(m_canon->m_finalZoomPoint.y)); #endif } xmlWriter.writeEndElement(); xmlWriter.writeStartElement("Images"); xmlWriter.writeTextElement("Frame", "Yes"); xmlWriter.writeEndElement(); xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); xmlFile.close(); } //----------------------------------------------------------------------------- bool StopMotion::loadXmlFile() { TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); bool webcam = false; bool foundCamera = false; QString text; int x; int y; std::wstring levelName = m_levelName.toStdWString(); TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath tempFile = parentDir + TFilePath(levelName + L".xml"); QString xmlFileName = tempFile.getQString(); QFile xmlFile(xmlFileName); if (!xmlFile.exists()) return false; xmlFile.open(QIODevice::ReadOnly); QXmlStreamReader xmlReader; xmlReader.setDevice(&xmlFile); xmlReader.readNext(); while (!xmlReader.atEnd()) { if (xmlReader.isStartElement()) { if (xmlReader.name() == "Webcam") { text = xmlReader.readElementText(); if (text == "yes") webcam = true; } if (xmlReader.name() == "CameraName") { text = xmlReader.readElementText(); if (webcam) { QList cameras = QCameraInfo::availableCameras(); if (cameras.size() > 0) { for (int i = 0; i < cameras.size(); i++) { if (cameras.at(i).description() == text) { changeCameras(i + 1); foundCamera = true; break; } } } } else { #ifdef WITH_CANON QString camName = ""; if (m_canon->getCameraCount() > 0) { m_canon->openCameraSession(); camName = QString::fromStdString(m_canon->getCameraName()); m_canon->closeCameraSession(); } if (text == camName) { changeCameras(-1); foundCamera = true; } #endif } } if (xmlReader.name() == "CameraResolutionX") { text = xmlReader.readElementText(); x = text.toInt(); } if (xmlReader.name() == "CameraResolutionY") { text = xmlReader.readElementText(); y = text.toInt(); if (foundCamera == true && webcam == true) { setWebcamResolution( QString(QString::number(x) + " x " + QString::number(y))); } } #ifdef WITH_CANON if (xmlReader.name() == "Aperture") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setAperture(text); } } if (xmlReader.name() == "ShutterSpeed") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setShutterSpeed(text); } } if (xmlReader.name() == "ISO") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setIso(text); } } if (xmlReader.name() == "PictureStyle") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setPictureStyle(text); } } if (xmlReader.name() == "ImageQuality") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setImageQuality(text); } } if (xmlReader.name() == "WhiteBalance") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setWhiteBalance(text); } } if (xmlReader.name() == "ColorTemperature") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setColorTemperature(text); } } if (xmlReader.name() == "ExposureCompensation") { text = xmlReader.readElementText(); if (foundCamera == true && webcam == false) { m_canon->setExposureCompensation(text); } } if (xmlReader.name() == "FocusCheckLocationX") { text = xmlReader.readElementText(); m_canon->m_finalZoomPoint.x = text.toInt(); } if (xmlReader.name() == "FocusCheckLocationY") { text = xmlReader.readElementText(); m_canon->m_finalZoomPoint.y = text.toInt(); } #endif } xmlReader.readNext(); } return true; } //----------------------------------------------------------------------------- bool StopMotion::exportImageSequence() { TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); std::wstring levelName = m_levelName.toStdWString(); if (levelName.empty()) { DVGui::error( tr("No level name specified: please choose a valid level name")); return false; } int frameNumber = m_frameNumber; TFilePath parentDir = scene->decodeFilePath(TFilePath(m_filePath)); TFilePath fullResFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_FullRes")); TFilePath liveViewFolder = scene->decodeFilePath( TFilePath(m_filePath) + TFilePath(levelName + L"_LiveView")); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); TFilePath actualLevelFp = scene->decodeFilePath(levelFp); TFilePath fullResFp = scene->decodeFilePath(fullResFolder + TFilePath(levelName + L"..jpg")); TFilePath fullResFile(fullResFp.withFrame(frameNumber)); TXshSimpleLevel *sl = 0; TXshLevel *level = scene->getLevelSet()->getLevel(levelName); if (level == NULL) { DVGui::error(tr("No level exists with the current name.")); return false; } /* if the existing level is not a raster level, then return */ if (level->getType() != OVL_XSHLEVEL) { DVGui::error(tr("This is not an image level.")); return false; } if (!level->getSimpleLevel()->getProperties()->isStopMotionLevel()) { DVGui::error(tr("This is not a stop motion level.")); return false; } sl = level->getSimpleLevel(); if (scene->decodeFilePath(sl->getPath()) != actualLevelFp) { DVGui::error( tr("The save in path specified does not match with the existing " "level.")); return false; } // find which column the level is on. // check with the first column int col = TApp::instance()->getCurrentColumn()->getColumnIndex(); int r0, r1, row; xsh->getColumn(col)->getRange(r0, r1); TXshSimpleLevel *colLevel = xsh->getCell(r0, col).getSimpleLevel(); if (colLevel != sl) { int cols = xsh->getColumnCount(); for (int i = 0; i < cols; i++) { xsh->getColumn(col)->getRange(r0, r1); colLevel = xsh->getCell(r0, col).getSimpleLevel(); if (colLevel == sl) { col = i; break; } } DVGui::error(tr("Could not find an xsheet level with the current level")); return false; } row = r0; xsh->getColumn(col)->getLevelRange(row, r0, r1); GenericSaveFilePopup *m_saveSequencePopup = new GenericSaveFilePopup("Export Image Sequence"); m_saveSequencePopup->setFileMode(true); TFilePath fp = m_saveSequencePopup->getPath(); if (fp == TFilePath()) { DVGui::error(tr("No export path given.")); return false; } TFilePath sourceFile; TFilePath exportFilePath = scene->decodeFilePath(fp + TFilePath(levelName + L"..jpg")); TFilePath exportFile; int exportFrameNumber = 1; for (int i = r0; i <= r1; i++) { int cellNumber = xsh->getCell(i, col).getFrameId().getNumber(); fullResFile = fullResFp.withFrame(cellNumber); if (TFileStatus(fullResFile).doesExist()) { sourceFile = fullResFile; } else { sourceFile = actualLevelFp.withFrame(cellNumber); } TFilePath sourceRawFile(sourceFile.getQString().replace( sourceFile.getQString().lastIndexOf("jpg"), 3, "cr2")); if (!TFileStatus(sourceFile).doesExist()) { DVGui::error(tr("Could not find the source file.")); return false; } exportFile = exportFilePath.withFrame(exportFrameNumber); TFilePath exportRawFile(exportFile.getQString().replace( exportFile.getQString().lastIndexOf("jpg"), 3, "cr2")); if (TFileStatus(exportFile).doesExist()) { QString question = tr("Overwrite existing files?"); int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"), QObject::tr("Cancel")); if (ret == 0 || ret == 2) return false; } TSystem::copyFile(exportFile, sourceFile); if (TSystem::doesExistFileOrLevel(sourceRawFile)) { TSystem::copyFile(exportRawFile, sourceRawFile); } exportFrameNumber++; if (!TFileStatus(exportFile).doesExist()) { DVGui::error(tr("An error occurred. Aborting.")); return false; } } QString message1 = tr("Successfully exported "); QString message2 = tr(" images."); QString finalMessage = message1 + QString::number(exportFrameNumber - 1) + message2; DVGui::MsgBoxInPopup(DVGui::MsgType(DVGui::INFORMATION), finalMessage); return true; } //----------------------------------------------------------------------------- // Refresh information that how many & which frames are saved for the current // level void StopMotion::refreshFrameInfo() { bool sessionOpen = false; #ifdef WITH_CANON sessionOpen = m_canon->m_sessionOpen; #endif if ((!sessionOpen && m_liveViewStatus < LiveViewOpen) && !m_usingWebcam) { m_frameInfoText = ""; return; } QString tooltipStr, labelStr; enum InfoType { NEW = 0, ADD, OVERWRITE, WARNING } infoType(WARNING); static QColor infoColors[4] = {Qt::cyan, Qt::green, Qt::yellow, Qt::red}; ToonzScene *currentScene = TApp::instance()->getCurrentScene()->getScene(); TLevelSet *levelSet = currentScene->getLevelSet(); std::wstring levelName = m_levelName.toStdWString(); int frameNumber = m_frameNumber; TDimension stopMotionRes; bool checkRes = true; if (m_usingWebcam) stopMotionRes = m_liveViewImageDimensions; #ifdef WITH_CANON if (!m_usingWebcam) { stopMotionRes = m_canon->m_proxyImageDimensions; if (m_canon->m_proxyImageDimensions == TDimension(0, 0)) { checkRes = false; } } #endif // else // stopMotionRes = m_fullImageDimensions; bool letterOptionEnabled = Preferences::instance()->isShowFrameNumberWithLettersEnabled(); // level with the same name TXshLevel *level_sameName = levelSet->getLevel(levelName); TFilePath levelFp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); // level with the same path TXshLevel *level_samePath = levelSet->getLevel(*(currentScene), levelFp); TFilePath actualLevelFp = currentScene->decodeFilePath(levelFp); // level existence bool levelExist = TSystem::doesExistFileOrLevel(actualLevelFp); // frame existence TFilePath frameFp(actualLevelFp.withFrame(frameNumber)); bool frameExist = false; if (levelExist) frameExist = TFileStatus(frameFp).doesExist(); // reset acceptable camera size m_allowedCameraSize = QSize(); // ### CASE 1 ### // If there is no same level registered in the scene cast if (!level_sameName && !level_samePath) { // If there is a level in the file system if (levelExist) { TLevelReaderP lr; TLevelP level_p; try { lr = TLevelReaderP(actualLevelFp); } catch (...) { // TODO: output something m_frameInfoText = tr("UNDEFINED WARNING"); return; } if (!lr) { // TODO: output something m_frameInfoText = tr("UNDEFINED WARNING"); return; } try { level_p = lr->loadInfo(); } catch (...) { // TODO: output something m_frameInfoText = tr("UNDEFINED WARNING"); return; } if (!level_p) { // TODO: output something m_frameInfoText = tr("UNDEFINED WARNING"); return; } int frameCount = level_p->getFrameCount(); TLevel::Iterator it = level_p->begin(); std::vector fids; for (int i = 0; it != level_p->end(); ++it, ++i) fids.push_back(it->first); tooltipStr += tr("The level is not registered in the scene, but exists in the file " "system."); // check resolution const TImageInfo *ii; try { ii = lr->getImageInfo(fids[0]); } catch (...) { // TODO: output something m_frameInfoText = tr("UNDEFINED WARNING"); return; } TDimension dim(ii->m_lx, ii->m_ly); // if the saved images has not the same resolution as the current camera // resolution if (checkRes && m_hasLiveViewImage && stopMotionRes != dim) { tooltipStr += tr("\nWARNING : Image size mismatch. The saved image " "size is %1 x %2.") .arg(dim.lx) .arg(dim.ly); labelStr += tr("WARNING "); infoType = WARNING; } // if the resolutions are matched { if (frameCount == 1) tooltipStr += tr("\nFrame %1 exists.") .arg(fidsToString(fids, letterOptionEnabled)); else tooltipStr += tr("\nFrames %1 exist.") .arg(fidsToString(fids, letterOptionEnabled)); // if the frame exists, then it will be overwritten if (frameExist) { labelStr += tr("OVERWRITE 1 of"); infoType = OVERWRITE; } else { labelStr += tr("ADD to"); infoType = ADD; } if (frameCount == 1) labelStr += tr(" %1 frame").arg(frameCount); else labelStr += tr(" %1 frames").arg(frameCount); } m_allowedCameraSize = QSize(dim.lx, dim.ly); } // If no level exists in the file system, then it will be a new level else { tooltipStr += tr("The level will be newly created."); labelStr += tr("NEW"); infoType = NEW; } } // ### CASE 2 ### // If there is already the level registered in the scene cast else if (level_sameName && level_samePath && level_sameName == level_samePath) { tooltipStr += tr("The level is already registered in the scene."); if (!levelExist) tooltipStr += tr("\nNOTE : The level is not saved."); std::vector fids; level_sameName->getFids(fids); // check resolution TDimension dim; bool ret = getRasterLevelSize(level_sameName, dim); if (!ret) { tooltipStr += tr("\nWARNING : Failed to get image size of the existing level %1.") .arg(QString::fromStdWString(levelName)); labelStr += tr("WARNING "); infoType = WARNING; } // if the saved images has not the same resolution as the current camera // resolution else if (checkRes && m_hasLiveViewImage && stopMotionRes != dim) { tooltipStr += tr("\nWARNING : Image size mismatch. The existing level " "size is %1 x %2.") .arg(dim.lx) .arg(dim.ly); labelStr += tr("WARNING "); infoType = WARNING; } // if the resolutions are matched { int frameCount = fids.size(); if (fids.size() == 1) tooltipStr += tr("\nFrame %1 exists.") .arg(fidsToString(fids, letterOptionEnabled)); else tooltipStr += tr("\nFrames %1 exist.") .arg(fidsToString(fids, letterOptionEnabled)); // Check if the target frame already exist in the level bool hasFrame = false; for (int f = 0; f < frameCount; f++) { if (fids.at(f).getNumber() == frameNumber) { hasFrame = true; break; } } // If there is already the frame then it will be overwritten if (hasFrame) { labelStr += tr("OVERWRITE 1 of"); infoType = OVERWRITE; } // Or, the frame will be added to the level else { labelStr += tr("ADD to"); infoType = ADD; } if (frameCount == 1) labelStr += tr(" %1 frame").arg(frameCount); else labelStr += tr(" %1 frames").arg(frameCount); } m_allowedCameraSize = QSize(dim.lx, dim.ly); } // ### CASE 3 ### // If there are some conflicts with the existing level. else { if (level_sameName) { TFilePath anotherPath = level_sameName->getPath(); tooltipStr += tr("WARNING : Level name conflicts. There already is a level %1 in the scene with the path\ \n %2.") .arg(QString::fromStdWString(levelName)) .arg(toQString(anotherPath)); // check resolution TDimension dim; bool ret = getRasterLevelSize(level_sameName, dim); if (ret && checkRes && m_hasLiveViewImage && stopMotionRes != dim) tooltipStr += tr("\nWARNING : Image size mismatch. The size of level " "with the same name is is %1 x %2.") .arg(dim.lx) .arg(dim.ly); m_allowedCameraSize = QSize(dim.lx, dim.ly); } if (level_samePath) { std::wstring anotherName = level_samePath->getName(); if (!tooltipStr.isEmpty()) tooltipStr += QString("\n"); tooltipStr += tr("WARNING : Level path conflicts. There already is a level with the path %1\ \n in the scene with the name %2.") .arg(toQString(levelFp)) .arg(QString::fromStdWString(anotherName)); // check resolution TDimension dim; bool ret = getRasterLevelSize(level_samePath, dim); if (ret && checkRes && m_hasLiveViewImage && stopMotionRes != dim) tooltipStr += tr("\nWARNING : Image size mismatch. The size of level " "with the same path is %1 x %2.") .arg(dim.lx) .arg(dim.ly); m_allowedCameraSize = QSize(dim.lx, dim.ly); } labelStr += tr("WARNING"); infoType = WARNING; } QColor infoColor = infoColors[(int)infoType]; m_infoColorName = infoColor.name(); m_frameInfoText = labelStr; m_frameInfoToolTip = tooltipStr; emit(frameInfoTextChanged(m_frameInfoText)); } //----------------------------------------------------------------------------- void StopMotion::updateLevelNameAndFrame(std::wstring levelName) { if (levelName != m_levelName.toStdWString()) { m_levelName = QString::fromStdWString(levelName); } m_liveViewImageMap.clear(); emit(levelNameChanged(m_levelName)); // m_previousLevelButton->setDisabled(levelName == L"A"); // set the start frame 10 if the option in preferences // "Show ABC Appendix to the Frame Number in Xsheet Cell" is active. // (frame 10 is displayed as "1" with this option) bool withLetter = Preferences::instance()->isShowFrameNumberWithLettersEnabled(); TLevelSet *levelSet = TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); TXshLevel *level_p = levelSet->getLevel(levelName); int startFrame; if (!level_p) { startFrame = withLetter ? 10 : 1; } else { std::vector fids; level_p->getFids(fids); if (fids.empty()) { startFrame = withLetter ? 10 : 1; } else { int lastNum = fids.back().getNumber(); startFrame = withLetter ? ((int)(lastNum / 10) + 1) * 10 : lastNum + 1; } } if (level_p) { TXshSimpleLevel *sl = level_p->getSimpleLevel(); if (sl && sl->getType() == OVL_XSHLEVEL && sl->getProperties()->isStopMotionLevel()) { buildLiveViewMap(sl); } } loadLineUpImage(); m_frameNumber = startFrame; emit(frameNumberChanged(startFrame)); refreshFrameInfo(); getSubsampling(); } //----------------------------------------------------------------------------- void StopMotion::setToNextNewLevel() { const std::unique_ptr nameBuilder(NameBuilder::getBuilder(L"")); TLevelSet *levelSet = TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); std::wstring levelName = L""; // Select a different unique level name in case it already exists (either in // scene or on disk) TFilePath fp; TFilePath actualFp; for (;;) { levelName = nameBuilder->getNext(); if (levelSet->getLevel(levelName) != 0) continue; fp = TFilePath(m_filePath) + TFilePath(levelName + L".." + m_fileType.toStdWString()); actualFp = scene->decodeFilePath(fp); if (TSystem::doesExistFileOrLevel(actualFp)) { continue; } break; } updateLevelNameAndFrame(levelName); } //----------------------------------------------------------------- void StopMotion::refreshCameraList() { QString camera = ""; bool hasCamera = false; #ifdef WITH_CANON if (m_canon->m_sessionOpen && m_canon->getCameraCount() > 0 && !m_usingWebcam && m_canon->m_cameraName == m_canon->getCameraName()) { hasCamera = true; camera = QString::fromStdString(m_canon->m_cameraName); } #endif if (m_usingWebcam) { QList cameras = QCameraInfo::availableCameras(); if (m_usingWebcam && cameras.size() > 0) { for (int i = 0; i < cameras.size(); i++) { if (cameras.at(i).description() == m_webcam->getWebcamDescription()) { hasCamera = true; camera = m_webcam->getWebcamDescription(); break; } } } } if (!hasCamera) disconnectAllCameras(); emit(updateCameraList(camera)); } //----------------------------------------------------------------- void StopMotion::changeCameras(int index) { // note: index is negative if this is called to load DSLR from load settings QList cameras = QCameraInfo::availableCameras(); // if selected the non-connected state, then disconnect the current camera if (index == 0) { disconnectAllCameras(); stopInterval(); return; } // There is a "Select Camera" as the first index index -= 1; // first see if the index didn't actually change if (cameras.size() > 0 && index < cameras.size() && index >= 0) { if (cameras.at(index).deviceName() == m_webcam->getWebcamDeviceName()) { return; } } #ifdef WITH_CANON if ((index > cameras.size() - 1) && m_canon->m_sessionOpen) return; #endif // close live view if open if (m_liveViewStatus > LiveViewClosed) { toggleLiveView(); } // Check if its a webcam or DSLR // Webcams are listed first, so see if one of them is selected if (index < 0 || index > cameras.size() - 1) { m_usingWebcam = false; } else { m_usingWebcam = true; m_webcam->setWebcamIndex(index); } // in case the camera is not changed if (m_usingWebcam) { #ifdef WITH_CANON if (m_canon->m_sessionOpen && m_canon->getCameraCount() > 0) { m_canon->closeCameraSession(); } #endif m_webcam->setWebcam(new QCamera(cameras.at(index))); m_webcam->setWebcamDeviceName(cameras.at(index).deviceName()); m_webcam->setWebcamDescription(cameras.at(index).description()); // loading new camera m_webcam->getWebcam()->load(); m_webcam->refreshWebcamResolutions(); QList webcamResolutions = m_webcam->getWebcamResolutions(); int sizeCount = webcamResolutions.count() - 1; int width; int height; for (int s = 0; s < webcamResolutions.size(); s++) { width = webcamResolutions.at(s).width(); height = webcamResolutions.at(s).height(); } // see if we can set the webcam resolution to the current camera resolution TDimension res = TApp::instance() ->getCurrentScene() ->getScene() ->getCurrentCamera() ->getRes(); if (webcamResolutions.contains(QSize(res.lx, res.ly))) { width = res.lx; height = res.ly; sizeCount = webcamResolutions.indexOf(QSize(res.lx, res.ly)); } m_webcam->getWebcam()->unload(); setWebcamResolution( QString(QString::number(width) + " x " + QString::number(height))); setTEnvCameraName(m_webcam->getWebcamDescription().toStdString()); emit(newCameraSelected(index + 1, true)); emit(webcamResolutionsChanged()); emit(newWebcamResolutionSelected(sizeCount)); } else { #ifdef WITH_CANON m_canon->openCameraSession(); setTEnvCameraName(m_canon->getCameraName()); if (index == -2) { index = cameras.size(); } m_webcam->clearWebcam(); emit(newCameraSelected(index + 1, false)); #endif } if (m_useNumpadShortcuts) toggleNumpadShortcuts(true); m_liveViewDpi = TPointD(0.0, 0.0); m_liveViewImageDimensions = TDimension(0, 0); m_hasLineUpImage = false; m_hasLiveViewImage = false; if (m_isTimeLapse && m_intervalStarted) { stopInterval(); } emit(liveViewStopped()); emit(liveViewChanged(false)); refreshFrameInfo(); // after all live view data is cleared, start it again. toggleLiveView(); } //----------------------------------------------------------------- void StopMotion::setWebcamResolution(QString resolution) { m_webcam->releaseWebcam(); // resolution is written in the itemText with the format " x // " (e.g. "800 x 600") QStringList texts = resolution.split(' '); // the split text must be "" "x" and "" if (texts.size() != 3) return; int tempStatus = m_liveViewStatus; m_liveViewStatus = LiveViewClosed; bool startTimer = false; if (m_timer->isActive()) { m_timer->stop(); startTimer = true; } qApp->processEvents(QEventLoop::AllEvents, 1000); m_webcam->setWebcamWidth(texts[0].toInt()); m_webcam->setWebcamHeight(texts[2].toInt()); m_liveViewDpi = TPointD(Stage::standardDpi, Stage::standardDpi); m_liveViewStatus = tempStatus; if (startTimer) m_timer->start(40); // update env setTEnvCameraResolution(resolution.toStdString()); refreshFrameInfo(); int index = m_webcam->getIndexOfResolution(); emit(newWebcamResolutionSelected(index)); } //----------------------------------------------------------------- bool StopMotion::toggleLiveView() { bool sessionOpen = false; #ifdef WITH_CANON sessionOpen = m_canon->m_sessionOpen; #endif if ((sessionOpen || m_usingWebcam) && m_liveViewStatus == LiveViewClosed) { m_liveViewDpi = TPointD(0.0, 0.0); m_liveViewImageDimensions = TDimension(0, 0); if (!m_usingWebcam) { #ifdef WITH_CANON m_canon->startCanonLiveView(); #endif } else m_liveViewStatus = LiveViewStarting; loadLineUpImage(); m_timer->start(40); emit(liveViewChanged(true)); Preferences::instance()->setValue(rewindAfterPlayback, false); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); return true; } else if ((sessionOpen || m_usingWebcam) && m_liveViewStatus > LiveViewClosed) { if (!m_usingWebcam) { #ifdef WITH_CANON m_canon->endCanonLiveView(); #endif } else { m_webcam->releaseWebcam(); } m_timer->stop(); emit(liveViewStopped()); emit(liveViewChanged(false)); if (m_turnOnRewind) { Preferences::instance()->setValue(rewindAfterPlayback, true); } TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); m_liveViewStatus = LiveViewClosed; return false; } else { DVGui::warning(tr("No camera selected.")); return false; } } //----------------------------------------------------------------- void StopMotion::pauseLiveView() { if (m_liveViewStatus == LiveViewOpen) { m_liveViewStatus = LiveViewPaused; m_userCalledPause = true; emit(liveViewStopped()); } else if (m_liveViewStatus == LiveViewPaused) { m_liveViewStatus = LiveViewOpen; m_userCalledPause = false; } else if (m_liveViewStatus == LiveViewClosed) { toggleLiveView(); } } //----------------------------------------------------------------- void StopMotion::toggleAlwaysUseLiveViewImages() { m_alwaysUseLiveViewImages = !m_alwaysUseLiveViewImages; emit(alwaysUseLiveViewImagesToggled(m_alwaysUseLiveViewImages)); TApp::instance()->getCurrentScene()->notifySceneChanged(); } //----------------------------------------------------------------- void StopMotion::onCanonCameraChanged(QString camera) { emit(cameraChanged(camera)); } //----------------------------------------------------------------- std::string StopMotion::getTEnvCameraName() { return StopMotionCameraName; } //----------------------------------------------------------------- void StopMotion::setTEnvCameraName(std::string name) { StopMotionCameraName = name; } //----------------------------------------------------------------- std::string StopMotion::getTEnvCameraResolution() { return StopMotionCameraResolution; } //----------------------------------------------------------------- void StopMotion::setTEnvCameraResolution(std::string resolution) { StopMotionCameraResolution = resolution; } //============================================================================= class StopMotionCaptureCommand : public MenuItemHandler { public: StopMotionCaptureCommand() : MenuItemHandler(MI_StopMotionCapture) {} void execute() { StopMotion *sm = StopMotion::instance(); if (sm->m_liveViewStatus > 0) sm->captureImage(); } } StopMotionCaptureCommand; //============================================================================= class StopMotionRaiseOpacityCommand : public MenuItemHandler { public: StopMotionRaiseOpacityCommand() : MenuItemHandler(MI_StopMotionRaiseOpacity) {} void execute() { StopMotion *sm = StopMotion::instance(); if (sm->m_liveViewStatus > 0) sm->raiseOpacity(); } } StopMotionRaiseOpacityCommand; //============================================================================= class StopMotionLowerOpacityCommand : public MenuItemHandler { public: StopMotionLowerOpacityCommand() : MenuItemHandler(MI_StopMotionLowerOpacity) {} void execute() { StopMotion *sm = StopMotion::instance(); if (sm->m_liveViewStatus > 0) sm->lowerOpacity(); } } StopMotionLowerOpacityCommand; //============================================================================= class StopMotionToggleLiveViewCommand : public MenuItemHandler { public: StopMotionToggleLiveViewCommand() : MenuItemHandler(MI_StopMotionToggleLiveView) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->pauseLiveView(); } } StopMotionToggleLiveViewCommand; //============================================================================= class StopMotionLowerSubsamplingCommand : public MenuItemHandler { public: StopMotionLowerSubsamplingCommand() : MenuItemHandler(MI_StopMotionLowerSubsampling) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->setSubsamplingValue(std::max(1, sm->getSubsamplingValue() - 1)); sm->setSubsampling(); } } StopMotionLowerSubsamplingCommand; //============================================================================= class StopMotionRaiseSubsamplingCommand : public MenuItemHandler { public: StopMotionRaiseSubsamplingCommand() : MenuItemHandler(MI_StopMotionRaiseSubsampling) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->setSubsamplingValue(std::min(30, sm->getSubsamplingValue() + 1)); sm->setSubsampling(); } } StopMotionRaiseSubsamplingCommand; //============================================================================= class StopMotionJumpToCameraCommand : public MenuItemHandler { public: StopMotionJumpToCameraCommand() : MenuItemHandler(MI_StopMotionJumpToCamera) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->jumpToCameraFrame(); } } StopMotionJumpToCameraCommand; //============================================================================= class StopMotionExportImageSequence : public MenuItemHandler { public: StopMotionExportImageSequence() : MenuItemHandler(MI_StopMotionExportImageSequence) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->exportImageSequence(); } } StopMotionExportImageSequence; //============================================================================= class StopMotionRemoveFrame : public MenuItemHandler { public: StopMotionRemoveFrame() : MenuItemHandler(MI_StopMotionRemoveFrame) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->removeStopMotionFrame(); } } StopMotionRemoveFrame; //============================================================================= class StopMotionNextFrame : public MenuItemHandler { public: StopMotionNextFrame() : MenuItemHandler(MI_StopMotionNextFrame) {} void execute() { StopMotion *sm = StopMotion::instance(); int index = TApp::instance()->getCurrentFrame()->getFrameIndex(); int maxInXSheet = TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount(); int max = std::max(maxInXSheet, sm->getXSheetFrameNumber() - 1); if (index < max) { TApp::instance()->getCurrentFrame()->setFrame(index + 1); } } } StopMotionNextFrame; //============================================================================= class StopMotionToggleUseLiveViewImagesCommand : public MenuItemHandler { public: StopMotionToggleUseLiveViewImagesCommand() : MenuItemHandler(MI_StopMotionToggleUseLiveViewImages) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->toggleAlwaysUseLiveViewImages(); } } StopMotionToggleUseLiveViewImagesCommand; #ifdef WITH_CANON //============================================================================= class StopMotionPickFocusCheck : public MenuItemHandler { public: StopMotionPickFocusCheck() : MenuItemHandler(MI_StopMotionPickFocusCheck) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->m_canon->toggleZoomPicking(); } } StopMotionPickFocusCheck; //============================================================================= class StopMotionToggleZoomCommand : public MenuItemHandler { public: StopMotionToggleZoomCommand() : MenuItemHandler(MI_StopMotionToggleZoom) {} void execute() { StopMotion *sm = StopMotion::instance(); sm->m_canon->zoomLiveView(); } } StopMotionToggleZoomCommand; #endif