From c8e57150ebdcac3325b47142f737e2c2e9d84750 Mon Sep 17 00:00:00 2001 From: "G.K.MacGregor" Date: Thu, 3 Jun 2021 22:26:54 +0100 Subject: [PATCH] Implement exporting PNG images --- mainwidget.cpp | 28 +++++++++++++++++++++++++ mainwidget.h | 2 ++ mainwindow.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++- mainwindow.h | 2 ++ render.h | 2 ++ 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/mainwidget.cpp b/mainwidget.cpp index 50d0f5e..c5ba646 100644 --- a/mainwidget.cpp +++ b/mainwidget.cpp @@ -140,6 +140,16 @@ void TeletextWidget::timerEvent(QTimerEvent *event) QWidget::timerEvent(event); } +void TeletextWidget::pauseFlash(bool pauseNow) +{ + if (pauseNow && m_flashTiming != 0) { + m_flashTimer.stop(); + m_flashPhase = 0; + update(); + } else if (m_flashTiming != 0) + m_flashTimer.start((m_flashTiming == 1) ? 500 : 167, this); +} + void TeletextWidget::setInsertMode(bool insertMode) { m_insertMode = insertMode; @@ -654,6 +664,24 @@ void LevelOneScene::toggleGrid(bool gridOn) m_sidePanelGridItemGroup[i]->setVisible(gridOn); } +void LevelOneScene::hideGUIElements(bool hidden) +{ + if (hidden) { + m_mainGridItemGroup->setVisible(false); + m_cursorRectItem->setVisible(false); + m_selectionRectItem->setVisible(false); + for (int i=0; i<32; i++) + if (m_sidePanelGridNeeded[i]) + m_sidePanelGridItemGroup[i]->setVisible(false); + } else { + if (static_cast(m_levelOneProxyWidget->widget())->document()->selectionActive()) + m_selectionRectItem->setVisible(true); + + m_cursorRectItem->setVisible(true); + toggleGrid(m_grid); + } +} + bool LevelOneScene::eventFilter(QObject *object, QEvent *event) { Q_UNUSED(object); diff --git a/mainwidget.h b/mainwidget.h index 5c61bd0..235c334 100644 --- a/mainwidget.h +++ b/mainwidget.h @@ -64,6 +64,7 @@ public slots: void toggleReveal(bool); void toggleMix(bool); void updateFlashTimer(int); + void pauseFlash(bool); void refreshRow(int); void setControlBit(int, bool); @@ -119,6 +120,7 @@ public slots: void updateCursor(); void updateSelection(); void toggleGrid(bool); + void hideGUIElements(bool); void setFullScreenColour(const QColor &); void setFullRowColour(int, const QColor &); diff --git a/mainwindow.cpp b/mainwindow.cpp index e691649..f12a8a7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -20,9 +20,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -120,6 +122,56 @@ bool MainWindow::saveAs() return saveFile(fileName); } +void MainWindow::exportPNG() +{ + QString exportFileName = QFileDialog::getSaveFileName(this, tr("Export PNG"), QString(), "PNG image (*.png)"); + if (exportFileName.isEmpty()) + return; + + // Prepare widget image for extraction + m_textWidget->pauseFlash(true); + m_textScene->hideGUIElements(true); + bool reshowCodes = m_textWidget->pageRender()->showCodes(); + if (reshowCodes) + m_textWidget->pageRender()->setShowCodes(false); + // Disable exporting in Mix mode as it corrupts the background + bool reMix = m_textWidget->pageRender()->mix(); + if (reMix) + m_textWidget->pageRender()->setMix(false); + + // Extract the image from the scene + QImage interImage = QImage(m_textScene->sceneRect().size().toSize(), QImage::Format_RGB32); +// This ought to make the background transparent in Mix mode, but it doesn't +// if (m_textWidget->pageRender()->mix()) +// interImage.fill(QColor(0, 0, 0, 0)); + QPainter interPainter(&interImage); + m_textScene->render(&interPainter); + + // Now we're extracted the image we can put the GUI things back + m_textScene->hideGUIElements(false); + if (reshowCodes) + m_textWidget->pageRender()->setShowCodes(true); + if (reMix) + m_textWidget->pageRender()->setMix(true); + m_textWidget->pauseFlash(false); + + // Now scale the extracted image to the selected aspect ratio + // We do this in two steps so that anti-aliasing only occurs on vertical lines + + // Double the vertical height first + const QImage doubleHeightImage = interImage.scaled(interImage.width(), interImage.height()*2, Qt::IgnoreAspectRatio, Qt::FastTransformation); + + // If aspect ratio is Pixel 1:2 we're already at the correct scale + if (m_viewAspectRatio != 3) { + // Scale it horizontally to the selected aspect ratio + const QImage scaledImage = doubleHeightImage.scaled((int)((float)doubleHeightImage.width() * aspectRatioHorizontalScaling[m_viewAspectRatio] * 2), doubleHeightImage.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + if (!scaledImage.save(exportFileName, "PNG")) + QMessageBox::warning(this, tr("QTeletextMaker"), tr("Cannot export file %1.").arg(QDir::toNativeSeparators(exportFileName))); + } else if (!doubleHeightImage.save(exportFileName, "PNG")) + QMessageBox::warning(this, tr("QTeletextMaker"), tr("Cannot export file %1.").arg(QDir::toNativeSeparators(exportFileName))); +} + void MainWindow::exportZXNet() { QDesktopServices::openUrl(QUrl("http://zxnet.co.uk/teletext/editor/" + exportHashStringPage(m_textWidget->document()->currentSubPage()) + exportHashStringPackets(m_textWidget->document()->currentSubPage()))); @@ -257,6 +309,10 @@ void MainWindow::createActions() setRecentFilesVisible(MainWindow::hasRecentFiles()); + QAction *exportPNGAct = fileMenu->addAction(tr("Export subpage as PNG...")); + exportPNGAct->setStatusTip("Export a PNG image of this subpage"); + connect(exportPNGAct, &QAction::triggered, this, &MainWindow::exportPNG); + QMenu *exportHashStringSubMenu = fileMenu->addMenu(tr("Export subpage to online editor")); QAction *exportZXNetAct = exportHashStringSubMenu->addAction(tr("Open in zxnet.co.uk")); @@ -555,7 +611,6 @@ void MainWindow::createActions() void MainWindow::setSceneDimensions() { - const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 }; const int topBottomBorders[3] = { 0, 10, 19 }; const int pillarBoxSizes[3] = { 672, 720, 854 }; const int leftRightBorders[3] = { 0, 24, 77 }; diff --git a/mainwindow.h b/mainwindow.h index f6c3113..f3e49b6 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -58,6 +58,7 @@ private slots: void open(); bool save(); bool saveAs(); + void exportPNG(); void exportZXNet(); void exportEditTF(); void updateRecentFileActions(); @@ -82,6 +83,7 @@ private slots: private: enum { m_MaxRecentFiles = 10 }; + const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 }; void init(); void createActions(); diff --git a/render.h b/render.h index 7a671d1..e5a402a 100644 --- a/render.h +++ b/render.h @@ -189,6 +189,8 @@ public: void decodePage(); void renderPage(); void renderPage(int r); + bool mix() const { return m_mix; }; + bool showCodes() const { return m_showCodes; }; void setTeletextPage(LevelOnePage *); void updateSidePanels(); void buildEnhanceMap(TextLayer *, int=0);