Enable screenshot and recording feature

This commit is contained in:
Miodrag Milanovic 2020-01-05 13:51:12 +01:00
parent abfe31d5d2
commit 3f439c1ef2
8 changed files with 111 additions and 2 deletions

View File

@ -161,6 +161,15 @@ Notes for developers
- To automatically format all source code, run `make clangformat`. - To automatically format all source code, run `make clangformat`.
- See the wiki for additional documentation on the architecture API. - See the wiki for additional documentation on the architecture API.
Recording a movie
-----------------
- To save a movie recording of place-and-route click recording icon in toolbar and select empty directory
where recording files will be stored and select frames to skip.
- Manualy start all PnR operations you wish
- Click on recording icon again to stop recording
- Go to directory containing files and exeecute `ffmpeg -f image2 -r 1 -i movie_%05d.png -c:v libx264 nextpnr.mp4`
Testing Testing
------- -------

View File

@ -28,5 +28,7 @@
<file>resources/wire.png</file> <file>resources/wire.png</file>
<file>resources/pip.png</file> <file>resources/pip.png</file>
<file>resources/group.png</file> <file>resources/group.png</file>
<file>resources/camera.png</file>
<file>resources/film.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -23,6 +23,7 @@
#include <QFileDialog> #include <QFileDialog>
#include <QGridLayout> #include <QGridLayout>
#include <QIcon> #include <QIcon>
#include <QImageWriter>
#include <QInputDialog> #include <QInputDialog>
#include <QSplitter> #include <QSplitter>
#include <fstream> #include <fstream>
@ -252,6 +253,18 @@ void BaseMainWindow::createMenusAndBars()
actionDisplayGroups->setChecked(true); actionDisplayGroups->setChecked(true);
connect(actionDisplayGroups, &QAction::triggered, this, &BaseMainWindow::enableDisableDecals); 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 // set initial state
fpgaView->enableDisableDecals(actionDisplayBel->isChecked(), actionDisplayWire->isChecked(), fpgaView->enableDisableDecals(actionDisplayBel->isChecked(), actionDisplayWire->isChecked(),
actionDisplayPip->isChecked(), actionDisplayGroups->isChecked()); actionDisplayPip->isChecked(), actionDisplayGroups->isChecked());
@ -317,6 +330,9 @@ void BaseMainWindow::createMenusAndBars()
deviceViewToolBar->addAction(actionDisplayWire); deviceViewToolBar->addAction(actionDisplayWire);
deviceViewToolBar->addAction(actionDisplayPip); deviceViewToolBar->addAction(actionDisplayPip);
deviceViewToolBar->addAction(actionDisplayGroups); deviceViewToolBar->addAction(actionDisplayGroups);
deviceViewToolBar->addSeparator();
deviceViewToolBar->addAction(actionScreenshot);
deviceViewToolBar->addAction(actionMovie);
// Add status bar with progress bar // Add status bar with progress bar
statusBar = new QStatusBar(); 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) void BaseMainWindow::pack_finished(bool status)
{ {
disableActions(); disableActions();

View File

@ -83,6 +83,9 @@ class BaseMainWindow : public QMainWindow
void taskStarted(); void taskStarted();
void taskPaused(); void taskPaused();
void screenshot();
void saveMovie();
Q_SIGNALS: Q_SIGNALS:
void contextChanged(Context *ctx); void contextChanged(Context *ctx);
void updateTreeView(); void updateTreeView();
@ -128,6 +131,9 @@ class BaseMainWindow : public QMainWindow
QAction *actionDisplayWire; QAction *actionDisplayWire;
QAction *actionDisplayPip; QAction *actionDisplayPip;
QAction *actionDisplayGroups; QAction *actionDisplayGroups;
QAction *actionScreenshot;
QAction *actionMovie;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -22,6 +22,9 @@
#include <QApplication> #include <QApplication>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QImageWriter>
#include <QMouseEvent> #include <QMouseEvent>
#include <QWidget> #include <QWidget>
@ -35,7 +38,7 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
FPGAViewWidget::FPGAViewWidget(QWidget *parent) 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) rendererArgs_(new FPGAViewWidget::RendererArgs), rendererData_(new FPGAViewWidget::RendererData)
{ {
colors_.background = QColor("#000000"); colors_.background = QColor("#000000");
@ -322,6 +325,23 @@ void FPGAViewWidget::paintGL()
lineShader_.draw(GraphicElement::STYLE_SELECTED, colors_.selected, thick11Px, matrix); lineShader_.draw(GraphicElement::STYLE_SELECTED, colors_.selected, thick11Px, matrix);
lineShader_.draw(GraphicElement::STYLE_HOVER, colors_.hovered, thick2Px, 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 // Render ImGui
QtImGui::newFrame(); QtImGui::newFrame();
QMutexLocker lock(&rendererArgsLock_); 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<DecalXY> decals, bool keep) void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals, bool keep)
{ {
{ {

View File

@ -120,13 +120,19 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
void zoomSelected(); void zoomSelected();
void zoomOutbound(); void zoomOutbound();
void enableDisableDecals(bool bels, bool wires, bool pips, bool groups); void enableDisableDecals(bool bels, bool wires, bool pips, bool groups);
void movieStart(QString dir, long frameSkip);
void movieStop();
Q_SIGNALS: Q_SIGNALS:
void clickedBel(BelId bel, bool add); void clickedBel(BelId bel, bool add);
void clickedWire(WireId wire, bool add); void clickedWire(WireId wire, bool add);
void clickedPip(PipId pip, bool add); void clickedPip(PipId pip, bool add);
private: private:
QString movieDir;
long currentMovieFrame;
long currentFrameSkip;
long movieCounter;
bool movieSaving;
const float zoomNear_ = 0.05f; // do not zoom closer than this const float zoomNear_ = 0.05f; // do not zoom closer than this
float zoomFar_ = 10.0f; // do not zoom further than this float zoomFar_ = 10.0f; // do not zoom further than this
const float zoomLvl1_ = 1.0f; const float zoomLvl1_ = 1.0f;

BIN
gui/resources/camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

BIN
gui/resources/film.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B