Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06970fd448 | ||
|
|
a3d4783796 | ||
|
|
c8e57150eb | ||
|
|
23c2623bcf |
@@ -5,6 +5,8 @@ Features
|
|||||||
- Load and save teletext pages in .tti format.
|
- Load and save teletext pages in .tti format.
|
||||||
- Rendering of teletext pages in Levels 1, 1.5, 2.5 and 3.5
|
- Rendering of teletext pages in Levels 1, 1.5, 2.5 and 3.5
|
||||||
- Rendering of Local Objects and side panels.
|
- Rendering of Local Objects and side panels.
|
||||||
|
- Export PNG images of teletext pages.
|
||||||
|
- Undo and redo of editing actions.
|
||||||
- Interactive X/26 Local Enhancement Data triplet editor.
|
- Interactive X/26 Local Enhancement Data triplet editor.
|
||||||
- Editing of X/27/4 and X/27/5 compositional links to enhancement data pages.
|
- Editing of X/27/4 and X/27/5 compositional links to enhancement data pages.
|
||||||
- Palette editor.
|
- Palette editor.
|
||||||
|
|||||||
2
main.cpp
2
main.cpp
@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
|
|||||||
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
||||||
QApplication::setOrganizationName("gkmac.co.uk");
|
QApplication::setOrganizationName("gkmac.co.uk");
|
||||||
QApplication::setOrganizationDomain("gkmac.co.uk");
|
QApplication::setOrganizationDomain("gkmac.co.uk");
|
||||||
QApplication::setApplicationVersion("0.2-alpha");
|
QApplication::setApplicationVersion("0.3-alpha");
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QApplication::applicationName());
|
parser.setApplicationDescription(QApplication::applicationName());
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
|
|||||||
@@ -140,6 +140,16 @@ void TeletextWidget::timerEvent(QTimerEvent *event)
|
|||||||
QWidget::timerEvent(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)
|
void TeletextWidget::setInsertMode(bool insertMode)
|
||||||
{
|
{
|
||||||
m_insertMode = insertMode;
|
m_insertMode = insertMode;
|
||||||
@@ -654,6 +664,24 @@ void LevelOneScene::toggleGrid(bool gridOn)
|
|||||||
m_sidePanelGridItemGroup[i]->setVisible(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<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionActive())
|
||||||
|
m_selectionRectItem->setVisible(true);
|
||||||
|
|
||||||
|
m_cursorRectItem->setVisible(true);
|
||||||
|
toggleGrid(m_grid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool LevelOneScene::eventFilter(QObject *object, QEvent *event)
|
bool LevelOneScene::eventFilter(QObject *object, QEvent *event)
|
||||||
{
|
{
|
||||||
Q_UNUSED(object);
|
Q_UNUSED(object);
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public slots:
|
|||||||
void toggleReveal(bool);
|
void toggleReveal(bool);
|
||||||
void toggleMix(bool);
|
void toggleMix(bool);
|
||||||
void updateFlashTimer(int);
|
void updateFlashTimer(int);
|
||||||
|
void pauseFlash(bool);
|
||||||
void refreshRow(int);
|
void refreshRow(int);
|
||||||
|
|
||||||
void setControlBit(int, bool);
|
void setControlBit(int, bool);
|
||||||
@@ -119,6 +120,7 @@ public slots:
|
|||||||
void updateCursor();
|
void updateCursor();
|
||||||
void updateSelection();
|
void updateSelection();
|
||||||
void toggleGrid(bool);
|
void toggleGrid(bool);
|
||||||
|
void hideGUIElements(bool);
|
||||||
void setFullScreenColour(const QColor &);
|
void setFullScreenColour(const QColor &);
|
||||||
void setFullRowColour(int, const QColor &);
|
void setFullRowColour(int, const QColor &);
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,11 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QImage>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QPainter>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
@@ -120,6 +122,56 @@ bool MainWindow::saveAs()
|
|||||||
return saveFile(fileName);
|
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've 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()
|
void MainWindow::exportZXNet()
|
||||||
{
|
{
|
||||||
QDesktopServices::openUrl(QUrl("http://zxnet.co.uk/teletext/editor/" + exportHashStringPage(m_textWidget->document()->currentSubPage()) + exportHashStringPackets(m_textWidget->document()->currentSubPage())));
|
QDesktopServices::openUrl(QUrl("http://zxnet.co.uk/teletext/editor/" + exportHashStringPage(m_textWidget->document()->currentSubPage()) + exportHashStringPackets(m_textWidget->document()->currentSubPage())));
|
||||||
@@ -166,7 +218,8 @@ void MainWindow::init()
|
|||||||
|
|
||||||
m_textView = new QGraphicsView(this);
|
m_textView = new QGraphicsView(this);
|
||||||
m_textView->setScene(m_textScene);
|
m_textView->setScene(m_textScene);
|
||||||
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
if (m_viewSmoothTransform)
|
||||||
|
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||||
m_textView->setBackgroundBrush(QBrush(QColor(32, 48, 96)));
|
m_textView->setBackgroundBrush(QBrush(QColor(32, 48, 96)));
|
||||||
setSceneDimensions();
|
setSceneDimensions();
|
||||||
setCentralWidget(m_textView);
|
setCentralWidget(m_textView);
|
||||||
@@ -257,14 +310,18 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
setRecentFilesVisible(MainWindow::hasRecentFiles());
|
setRecentFilesVisible(MainWindow::hasRecentFiles());
|
||||||
|
|
||||||
QMenu *exportHashStringSubMenu = fileMenu->addMenu(tr("Export to online editor"));
|
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"));
|
QAction *exportZXNetAct = exportHashStringSubMenu->addAction(tr("Open in zxnet.co.uk"));
|
||||||
exportZXNetAct->setStatusTip("Export and open page in zxnet.co.uk online editor");
|
exportZXNetAct->setStatusTip("Export and open this subpage in the zxnet.co.uk online editor");
|
||||||
connect(exportZXNetAct, &QAction::triggered, this, &MainWindow::exportZXNet);
|
connect(exportZXNetAct, &QAction::triggered, this, &MainWindow::exportZXNet);
|
||||||
|
|
||||||
QAction *exportEditTFAct = exportHashStringSubMenu->addAction(tr("Open in edit.tf"));
|
QAction *exportEditTFAct = exportHashStringSubMenu->addAction(tr("Open in edit.tf"));
|
||||||
exportEditTFAct->setStatusTip("Export and open page in edit.tf online editor");
|
exportEditTFAct->setStatusTip("Export and open this subpage in the edit.tf online editor");
|
||||||
connect(exportEditTFAct, &QAction::triggered, this, &MainWindow::exportEditTF);
|
connect(exportEditTFAct, &QAction::triggered, this, &MainWindow::exportEditTF);
|
||||||
|
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
@@ -418,6 +475,13 @@ void MainWindow::createActions()
|
|||||||
borderGroup->addAction(m_borderActs[i]);
|
borderGroup->addAction(m_borderActs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
|
m_smoothTransformAction = viewMenu->addAction(tr("Smooth font scaling"));
|
||||||
|
m_smoothTransformAction->setCheckable(true);
|
||||||
|
m_smoothTransformAction->setStatusTip(tr("Toggle smooth font scaling"));
|
||||||
|
connect(m_smoothTransformAction, &QAction::toggled, this, &MainWindow::setSmoothTransform);
|
||||||
|
|
||||||
QAction *zoomInAct = viewMenu->addAction(tr("Zoom In"));
|
QAction *zoomInAct = viewMenu->addAction(tr("Zoom In"));
|
||||||
zoomInAct->setShortcuts(QKeySequence::ZoomIn);
|
zoomInAct->setShortcuts(QKeySequence::ZoomIn);
|
||||||
zoomInAct->setStatusTip(tr("Zoom in"));
|
zoomInAct->setStatusTip(tr("Zoom in"));
|
||||||
@@ -555,7 +619,6 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
void MainWindow::setSceneDimensions()
|
void MainWindow::setSceneDimensions()
|
||||||
{
|
{
|
||||||
const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 };
|
|
||||||
const int topBottomBorders[3] = { 0, 10, 19 };
|
const int topBottomBorders[3] = { 0, 10, 19 };
|
||||||
const int pillarBoxSizes[3] = { 672, 720, 854 };
|
const int pillarBoxSizes[3] = { 672, 720, 854 };
|
||||||
const int leftRightBorders[3] = { 0, 24, 77 };
|
const int leftRightBorders[3] = { 0, 24, 77 };
|
||||||
@@ -612,6 +675,15 @@ void MainWindow::setAspectRatio(int newViewAspectRatio)
|
|||||||
setSceneDimensions();
|
setSceneDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setSmoothTransform(bool smoothTransform)
|
||||||
|
{
|
||||||
|
m_viewSmoothTransform = smoothTransform;
|
||||||
|
if (smoothTransform)
|
||||||
|
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||||
|
else
|
||||||
|
m_textView->setRenderHints({ });
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::zoomIn()
|
void MainWindow::zoomIn()
|
||||||
{
|
{
|
||||||
if (m_viewZoom < 4)
|
if (m_viewZoom < 4)
|
||||||
@@ -705,6 +777,10 @@ void MainWindow::readSettings()
|
|||||||
m_viewAspectRatio = settings.value("aspectratio", 0).toInt();
|
m_viewAspectRatio = settings.value("aspectratio", 0).toInt();
|
||||||
m_viewAspectRatio = (m_viewAspectRatio < 0 || m_viewAspectRatio > 2) ? 0 : m_viewAspectRatio;
|
m_viewAspectRatio = (m_viewAspectRatio < 0 || m_viewAspectRatio > 2) ? 0 : m_viewAspectRatio;
|
||||||
m_aspectRatioActs[m_viewAspectRatio]->setChecked(true);
|
m_aspectRatioActs[m_viewAspectRatio]->setChecked(true);
|
||||||
|
m_viewSmoothTransform = settings.value("smoothTransform", 0).toBool();
|
||||||
|
m_smoothTransformAction->blockSignals(true);
|
||||||
|
m_smoothTransformAction->setChecked(m_viewSmoothTransform);
|
||||||
|
m_smoothTransformAction->blockSignals(false);
|
||||||
m_viewZoom = settings.value("zoom", 2).toInt();
|
m_viewZoom = settings.value("zoom", 2).toInt();
|
||||||
m_viewZoom = (m_viewZoom < 0 || m_viewZoom > 4) ? 2 : m_viewZoom;
|
m_viewZoom = (m_viewZoom < 0 || m_viewZoom > 4) ? 2 : m_viewZoom;
|
||||||
|
|
||||||
@@ -743,6 +819,7 @@ void MainWindow::writeSettings()
|
|||||||
settings.setValue("windowState", saveState());
|
settings.setValue("windowState", saveState());
|
||||||
settings.setValue("border", m_viewBorder);
|
settings.setValue("border", m_viewBorder);
|
||||||
settings.setValue("aspectratio", m_viewAspectRatio);
|
settings.setValue("aspectratio", m_viewAspectRatio);
|
||||||
|
settings.setValue("smoothTransform", m_viewSmoothTransform);
|
||||||
settings.setValue("zoom", m_viewZoom);
|
settings.setValue("zoom", m_viewZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ private slots:
|
|||||||
void open();
|
void open();
|
||||||
bool save();
|
bool save();
|
||||||
bool saveAs();
|
bool saveAs();
|
||||||
|
void exportPNG();
|
||||||
void exportZXNet();
|
void exportZXNet();
|
||||||
void exportEditTF();
|
void exportEditTF();
|
||||||
void updateRecentFileActions();
|
void updateRecentFileActions();
|
||||||
@@ -74,6 +75,7 @@ private slots:
|
|||||||
void setSceneDimensions();
|
void setSceneDimensions();
|
||||||
void setBorder(int);
|
void setBorder(int);
|
||||||
void setAspectRatio(int);
|
void setAspectRatio(int);
|
||||||
|
void setSmoothTransform(bool);
|
||||||
void zoomIn();
|
void zoomIn();
|
||||||
void zoomOut();
|
void zoomOut();
|
||||||
void zoomReset();
|
void zoomReset();
|
||||||
@@ -82,6 +84,7 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
enum { m_MaxRecentFiles = 10 };
|
enum { m_MaxRecentFiles = 10 };
|
||||||
|
const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 };
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void createActions();
|
void createActions();
|
||||||
@@ -104,6 +107,7 @@ private:
|
|||||||
QGraphicsView *m_textView;
|
QGraphicsView *m_textView;
|
||||||
|
|
||||||
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
||||||
|
bool m_viewSmoothTransform;
|
||||||
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
||||||
PageEnhancementsDockWidget *m_pageEnhancementsDockWidget;
|
PageEnhancementsDockWidget *m_pageEnhancementsDockWidget;
|
||||||
X26DockWidget *m_x26DockWidget;
|
X26DockWidget *m_x26DockWidget;
|
||||||
@@ -115,6 +119,7 @@ private:
|
|||||||
QAction *m_deleteSubPageAction;
|
QAction *m_deleteSubPageAction;
|
||||||
QAction *m_borderActs[3];
|
QAction *m_borderActs[3];
|
||||||
QAction *m_aspectRatioActs[4];
|
QAction *m_aspectRatioActs[4];
|
||||||
|
QAction *m_smoothTransformAction;
|
||||||
|
|
||||||
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
||||||
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
||||||
|
|||||||
2
render.h
2
render.h
@@ -189,6 +189,8 @@ public:
|
|||||||
void decodePage();
|
void decodePage();
|
||||||
void renderPage();
|
void renderPage();
|
||||||
void renderPage(int r);
|
void renderPage(int r);
|
||||||
|
bool mix() const { return m_mix; };
|
||||||
|
bool showCodes() const { return m_showCodes; };
|
||||||
void setTeletextPage(LevelOnePage *);
|
void setTeletextPage(LevelOnePage *);
|
||||||
void updateSidePanels();
|
void updateSidePanels();
|
||||||
void buildEnhanceMap(TextLayer *, int=0);
|
void buildEnhanceMap(TextLayer *, int=0);
|
||||||
|
|||||||
Reference in New Issue
Block a user