From 3f439c1ef2c1f4b7694d05be53117f0afe97cc17 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 5 Jan 2020 13:51:12 +0100 Subject: Enable screenshot and recording feature --- gui/base.qrc | 2 ++ gui/basewindow.cc | 50 +++++++++++++++++++++++++++++++++++++++++++++++ gui/basewindow.h | 6 ++++++ gui/fpgaviewwidget.cc | 38 ++++++++++++++++++++++++++++++++++- gui/fpgaviewwidget.h | 8 +++++++- gui/resources/camera.png | Bin 0 -> 665 bytes gui/resources/film.png | Bin 0 -> 653 bytes 7 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 gui/resources/camera.png create mode 100644 gui/resources/film.png (limited to 'gui') diff --git a/gui/base.qrc b/gui/base.qrc index 63612bf4..0671fa9e 100644 --- a/gui/base.qrc +++ b/gui/base.qrc @@ -28,5 +28,7 @@ resources/wire.png resources/pip.png resources/group.png + resources/camera.png + resources/film.png diff --git a/gui/basewindow.cc b/gui/basewindow.cc index c3dbc131..0bfc59a3 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -252,6 +253,18 @@ void BaseMainWindow::createMenusAndBars() actionDisplayGroups->setChecked(true); connect(actionDisplayGroups, &QAction::triggered, this, &BaseMainWindow::enableDisableDecals); + actionScreenshot = new QAction("Screenshot", this); + actionScreenshot->setIcon(QIcon(":/icons/resources/camera.png")); + actionScreenshot->setStatusTip("Taking a screenshot"); + connect(actionScreenshot, &QAction::triggered, this, &BaseMainWindow::screenshot); + + actionMovie = new QAction("Recording", this); + actionMovie->setIcon(QIcon(":/icons/resources/film.png")); + actionMovie->setStatusTip("Saving a movie"); + actionMovie->setCheckable(true); + actionMovie->setChecked(false); + connect(actionMovie, &QAction::triggered, this, &BaseMainWindow::saveMovie); + // set initial state fpgaView->enableDisableDecals(actionDisplayBel->isChecked(), actionDisplayWire->isChecked(), actionDisplayPip->isChecked(), actionDisplayGroups->isChecked()); @@ -317,6 +330,9 @@ void BaseMainWindow::createMenusAndBars() deviceViewToolBar->addAction(actionDisplayWire); deviceViewToolBar->addAction(actionDisplayPip); deviceViewToolBar->addAction(actionDisplayGroups); + deviceViewToolBar->addSeparator(); + deviceViewToolBar->addAction(actionScreenshot); + deviceViewToolBar->addAction(actionMovie); // Add status bar with progress bar statusBar = new QStatusBar(); @@ -362,6 +378,40 @@ void BaseMainWindow::save_json() } } +void BaseMainWindow::screenshot() +{ + QString fileName = QFileDialog::getSaveFileName(this, QString("Save screenshot"), QString(), QString("*.png")); + if (!fileName.isEmpty()) { + QImage image = fpgaView->grabFramebuffer(); + if (!fileName.endsWith(".png")) + fileName += ".png"; + QImageWriter imageWriter(fileName, "png"); + if (imageWriter.write(image)) + log("Saving screenshot successful.\n"); + else + log("Saving screenshot failed.\n"); + } +} + +void BaseMainWindow::saveMovie() +{ + if (actionMovie->isChecked()) { + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Movie Directory"), QDir::currentPath(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + if (!dir.isEmpty()) { + bool ok; + int frames = QInputDialog::getInt(this, "Skip frames", tr("Frames to skip (1 frame = 50ms):"), 5, 0, 1000, + 1, &ok); + if (ok) + fpgaView->movieStart(dir, frames); + else + actionMovie->setChecked(false); + } else + actionMovie->setChecked(false); + } else { + fpgaView->movieStop(); + } +} void BaseMainWindow::pack_finished(bool status) { disableActions(); diff --git a/gui/basewindow.h b/gui/basewindow.h index 7562307e..fe9dfdf2 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -83,6 +83,9 @@ class BaseMainWindow : public QMainWindow void taskStarted(); void taskPaused(); + void screenshot(); + void saveMovie(); + Q_SIGNALS: void contextChanged(Context *ctx); void updateTreeView(); @@ -128,6 +131,9 @@ class BaseMainWindow : public QMainWindow QAction *actionDisplayWire; QAction *actionDisplayPip; QAction *actionDisplayGroups; + + QAction *actionScreenshot; + QAction *actionMovie; }; NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 2e1bbf63..8730c9d9 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include #include @@ -35,7 +38,7 @@ NEXTPNR_NAMESPACE_BEGIN FPGAViewWidget::FPGAViewWidget(QWidget *parent) - : QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this), lineShader_(this), zoom_(10.0f), + : QOpenGLWidget(parent), movieSaving(false), ctx_(nullptr), paintTimer_(this), lineShader_(this), zoom_(10.0f), rendererArgs_(new FPGAViewWidget::RendererArgs), rendererData_(new FPGAViewWidget::RendererData) { colors_.background = QColor("#000000"); @@ -322,6 +325,23 @@ void FPGAViewWidget::paintGL() lineShader_.draw(GraphicElement::STYLE_SELECTED, colors_.selected, thick11Px, matrix); lineShader_.draw(GraphicElement::STYLE_HOVER, colors_.hovered, thick2Px, matrix); + if (movieSaving) { + if (movieCounter == currentFrameSkip) { + QMutexLocker lock(&rendererArgsLock_); + movieCounter = 0; + currentMovieFrame++; + + QImage image = grabFramebuffer(); + QString number = QString("movie_%1.png").arg(currentMovieFrame, 5, 10, QChar('0')); + + QFileInfo fileName = QFileInfo(QDir(movieDir), number); + QImageWriter imageWriter(fileName.absoluteFilePath(), "png"); + imageWriter.write(image); + } else { + movieCounter++; + } + } + // Render ImGui QtImGui::newFrame(); QMutexLocker lock(&rendererArgsLock_); @@ -579,6 +599,22 @@ void FPGAViewWidget::renderLines(void) } } +void FPGAViewWidget::movieStart(QString dir, long frameSkip) +{ + QMutexLocker locker(&rendererArgsLock_); + movieDir = dir; + currentMovieFrame = 0; + movieCounter = 0; + currentFrameSkip = frameSkip; + movieSaving = true; +} + +void FPGAViewWidget::movieStop() +{ + QMutexLocker locker(&rendererArgsLock_); + movieSaving = false; +} + void FPGAViewWidget::onSelectedArchItem(std::vector decals, bool keep) { { diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index 735590ba..7f99408e 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -120,13 +120,19 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions void zoomSelected(); void zoomOutbound(); void enableDisableDecals(bool bels, bool wires, bool pips, bool groups); - + void movieStart(QString dir, long frameSkip); + void movieStop(); Q_SIGNALS: void clickedBel(BelId bel, bool add); void clickedWire(WireId wire, bool add); void clickedPip(PipId pip, bool add); private: + QString movieDir; + long currentMovieFrame; + long currentFrameSkip; + long movieCounter; + bool movieSaving; const float zoomNear_ = 0.05f; // do not zoom closer than this float zoomFar_ = 10.0f; // do not zoom further than this const float zoomLvl1_ = 1.0f; diff --git a/gui/resources/camera.png b/gui/resources/camera.png new file mode 100644 index 00000000..8536d1a7 Binary files /dev/null and b/gui/resources/camera.png differ diff --git a/gui/resources/film.png b/gui/resources/film.png new file mode 100644 index 00000000..b0ce7bb1 Binary files /dev/null and b/gui/resources/film.png differ -- cgit v1.2.3