From 61a2cf986b03f08ad79c4f713c4bf0ee91fcabe0 Mon Sep 17 00:00:00 2001 From: "Joakim L. Gilje" Date: Tue, 7 Dec 2021 13:47:42 +0100 Subject: [PATCH] implemented fullscreen stretch modes --- src/qt/CMakeLists.txt | 2 + src/qt/qt_hardwarerenderer.cpp | 20 +++----- src/qt/qt_hardwarerenderer.hpp | 9 ++-- src/qt/qt_mainwindow.cpp | 55 +++++++++++++++++++++ src/qt/qt_mainwindow.hpp | 4 ++ src/qt/qt_mainwindow.ui | 16 +++--- src/qt/qt_renderercomon.cpp | 89 ++++++++++++++++++++++++++++++++++ src/qt/qt_renderercomon.hpp | 19 ++++++++ src/qt/qt_softwarerenderer.cpp | 21 +++----- src/qt/qt_softwarerenderer.hpp | 9 ++-- 10 files changed, 201 insertions(+), 43 deletions(-) create mode 100644 src/qt/qt_renderercomon.cpp create mode 100644 src/qt/qt_renderercomon.hpp diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index a577bcddc..7936e3a42 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -21,6 +21,8 @@ add_library(ui STATIC qt_rendererstack.cpp qt_rendererstack.hpp qt_rendererstack.ui + qt_renderercomon.cpp + qt_renderercomon.hpp qt_softwarerenderer.cpp qt_softwarerenderer.hpp qt_hardwarerenderer.cpp diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp index d9a206238..6efa4809c 100644 --- a/src/qt/qt_hardwarerenderer.cpp +++ b/src/qt/qt_hardwarerenderer.cpp @@ -14,14 +14,8 @@ void HardwareRenderer::initializeGL() initializeOpenGLFunctions(); } -void HardwareRenderer::paintGL() -{ - QPainter painter(this); - painter.setRenderHint(QPainter::SmoothPixmapTransform, video_filter_method > 0 ? true : false); - painter.drawImage(QRect(0, 0, width(), height()), image, QRect(sx, sy, sw, sh)); - // "release" image, reducing it's refcount, so renderstack::blit() - // won't have to reallocate - image = QImage(); +void HardwareRenderer::paintGL() { + onPaint(this); } void HardwareRenderer::setRenderType(RenderType type) { @@ -41,9 +35,11 @@ void HardwareRenderer::setRenderType(RenderType type) { void HardwareRenderer::onBlit(const QImage& img, int x, int y, int w, int h) { image = img; - sx = x; - sy = y; - sw = w; - sh = h; + source.setRect(x, y, w, h); update(); } + +void HardwareRenderer::resizeEvent(QResizeEvent *event) { + onResize(width(), height()); + QOpenGLWidget::resizeEvent(event); +} diff --git a/src/qt/qt_hardwarerenderer.hpp b/src/qt/qt_hardwarerenderer.hpp index 4d7f68a08..3a47c6284 100644 --- a/src/qt/qt_hardwarerenderer.hpp +++ b/src/qt/qt_hardwarerenderer.hpp @@ -10,11 +10,13 @@ #include #include +#include "qt_renderercomon.hpp" + #ifdef WAYLAND #include "wl_mouse.hpp" #endif -class HardwareRenderer : public QOpenGLWidget, protected QOpenGLFunctions +class HardwareRenderer : public QOpenGLWidget, protected QOpenGLFunctions, public RendererCommon { Q_OBJECT @@ -49,7 +51,6 @@ public: public slots: void onBlit(const QImage& img, int, int, int, int); -private: - QImage image; - int sx, sy, sw, sh; +protected: + void resizeEvent(QResizeEvent *event) override; }; diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 3667a2b59..e56967892 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -139,6 +139,20 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionLinear->setChecked(true); break; } + switch (video_fullscreen_scale) { + case FULLSCR_SCALE_FULL: + ui->actionFullScreen_stretch->setChecked(true); + break; + case FULLSCR_SCALE_43: + ui->actionFullScreen_43->setChecked(true); + break; + case FULLSCR_SCALE_KEEPRATIO: + ui->actionFullScreen_keepRatio->setChecked(true); + break; + case FULLSCR_SCALE_INT: + ui->actionFullScreen_int->setChecked(true); + break; + } setFocusPolicy(Qt::StrongFocus); ui->gles->setFocusPolicy(Qt::NoFocus); @@ -735,6 +749,10 @@ void MainWindow::on_actionFullscreen_triggered() { showFullScreen(); video_fullscreen = 1; } + + auto widget = ui->stackedWidget->currentWidget(); + auto rc = dynamic_cast(widget); + rc->onResize(widget->width(), widget->height()); } void MainWindow::setTitle_(const wchar_t *title) @@ -932,3 +950,40 @@ void MainWindow::on_actionLinear_triggered() { video_filter_method = 1; ui->actionNearest->setChecked(false); } + +static void update_fullscreen_scale_checkboxes(Ui::MainWindow* ui, QAction* selected) { + ui->actionFullScreen_stretch->setChecked(ui->actionFullScreen_stretch == selected); + ui->actionFullScreen_43->setChecked(ui->actionFullScreen_43 == selected); + ui->actionFullScreen_keepRatio->setChecked(ui->actionFullScreen_keepRatio == selected); + ui->actionFullScreen_int->setChecked(ui->actionFullScreen_int == selected); + + if (video_fullscreen > 0) { + auto widget = ui->stackedWidget->currentWidget(); + auto rc = dynamic_cast(widget); + rc->onResize(widget->width(), widget->height()); + } + + device_force_redraw(); + config_save(); +} + +void MainWindow::on_actionFullScreen_stretch_triggered() { + video_fullscreen_scale = FULLSCR_SCALE_FULL; + update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_stretch); +} + +void MainWindow::on_actionFullScreen_43_triggered() { + video_fullscreen_scale = FULLSCR_SCALE_43; + update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_43); +} + +void MainWindow::on_actionFullScreen_keepRatio_triggered() { + video_fullscreen_scale = FULLSCR_SCALE_KEEPRATIO; + update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_keepRatio); +} + +void MainWindow::on_actionFullScreen_int_triggered() { + video_fullscreen_scale = FULLSCR_SCALE_INT; + update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_int); +} + diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index e3a80b15d..406d9a26a 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -64,6 +64,10 @@ private slots: void on_action2x_triggered(); void on_actionLinear_triggered(); void on_actionNearest_triggered(); + void on_actionFullScreen_int_triggered(); + void on_actionFullScreen_keepRatio_triggered(); + void on_actionFullScreen_43_triggered(); + void on_actionFullScreen_stretch_triggered(); void refreshMediaMenu(); void showMessage_(const QString& header, const QString& message); diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index fb53af568..af8a8a2bd 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -111,10 +111,10 @@ Fullscreen stretch mode - - - - + + + + @@ -362,7 +362,7 @@ Linear - + true @@ -370,7 +370,7 @@ Full screen stretch - + true @@ -378,7 +378,7 @@ 4:3 - + true @@ -386,7 +386,7 @@ Square pixels (Keep ratio) - + true diff --git a/src/qt/qt_renderercomon.cpp b/src/qt/qt_renderercomon.cpp new file mode 100644 index 000000000..173ae1abe --- /dev/null +++ b/src/qt/qt_renderercomon.cpp @@ -0,0 +1,89 @@ +#include "qt_renderercomon.hpp" + +#include +#include + +#include + +extern "C" { +#include <86box/86box.h> +#include <86box/video.h> +} + +RendererCommon::RendererCommon() = default; + +void RendererCommon::onPaint(QPaintDevice* device) { + QPainter painter(device); + painter.setRenderHint(QPainter::SmoothPixmapTransform, video_filter_method > 0 ? true : false); + painter.drawImage(destination, image, source); + // "release" image, reducing it's refcount, so renderstack::blit() + // won't have to reallocate + image = QImage(); +} + +static void integer_scale(double *d, double *g) { + double ratio; + + if (*d > *g) { + ratio = std::floor(*d / *g); + *d = *g * ratio; + } else { + ratio = std::ceil(*d / *g); + *d = *g / ratio; + } +} + +void RendererCommon::onResize(int width, int height) { + if (video_fullscreen == 0) { + destination.setRect(0, 0, width, height); + return; + } + double dx, dy, dw, dh, gsr; + + double hw = width; + double hh = height; + double gw = source.width(); + double gh = source.height(); + double hsr = hw / hh; + + switch (video_fullscreen_scale) { + case FULLSCR_SCALE_INT: + gsr = gw / gh; + if (gsr <= hsr) { + dw = hh * gsr; + dh = hh; + } else { + dw = hw; + dh = hw / gsr; + } + integer_scale(&dw, &gw); + integer_scale(&dh, &gh); + dx = (hw - dw) / 2.0; + dy = (hh - dh) / 2.0; + destination.setRect(dx, dy, dw, dh); + break; + case FULLSCR_SCALE_43: + case FULLSCR_SCALE_KEEPRATIO: + if (video_fullscreen_scale == FULLSCR_SCALE_43) { + gsr = 4.0 / 3.0; + } else { + gsr = gw / gh; + } + + if (gsr <= hsr) { + dw = hh * gsr; + dh = hh; + } else { + dw = hw; + dh = hw / gsr; + } + dx = (hw - dw) / 2.0; + dy = (hh - dh) / 2.0; + destination.setRect(dx, dy, dw, dh); + break; + case FULLSCR_SCALE_FULL: + default: + destination.setRect(0, 0, hw, hh); + break; + } +} diff --git a/src/qt/qt_renderercomon.hpp b/src/qt/qt_renderercomon.hpp new file mode 100644 index 000000000..6eed9c9b2 --- /dev/null +++ b/src/qt/qt_renderercomon.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +class QWidget; + +class RendererCommon +{ +public: + RendererCommon(); + + void onResize(int width, int height); +protected: + void onPaint(QPaintDevice* device); + + QImage image; + QRect source, destination; +}; diff --git a/src/qt/qt_softwarerenderer.cpp b/src/qt/qt_softwarerenderer.cpp index cf69ca2fb..0565d2d6c 100644 --- a/src/qt/qt_softwarerenderer.cpp +++ b/src/qt/qt_softwarerenderer.cpp @@ -1,26 +1,19 @@ #include "qt_softwarerenderer.hpp" -extern "C" { -#include <86box/86box.h> -} -#include - SoftwareRenderer::SoftwareRenderer(QWidget *parent) : QWidget(parent) {} void SoftwareRenderer::paintEvent(QPaintEvent *event) { (void) event; - - QPainter painter(this); - painter.setRenderHint(QPainter::SmoothPixmapTransform, video_filter_method > 0 ? true : false); - painter.drawImage(QRect(0, 0, width(), height()), image, QRect(sx, sy, sw, sh)); - image = QImage(); + onPaint(this); } void SoftwareRenderer::onBlit(const QImage& img, int x, int y, int w, int h) { image = img; - sx = x; - sy = y; - sw = w; - sh = h; + source.setRect(x, y, w, h); update(); } + +void SoftwareRenderer::resizeEvent(QResizeEvent *event) { + onResize(width(), height()); + QWidget::resizeEvent(event); +} diff --git a/src/qt/qt_softwarerenderer.hpp b/src/qt/qt_softwarerenderer.hpp index 2a1075023..873e9f732 100644 --- a/src/qt/qt_softwarerenderer.hpp +++ b/src/qt/qt_softwarerenderer.hpp @@ -2,21 +2,20 @@ #define SOFTWARERENDERER_HPP #include +#include "qt_renderercomon.hpp" -class SoftwareRenderer : public QWidget +class SoftwareRenderer : public QWidget, public RendererCommon { Q_OBJECT public: explicit SoftwareRenderer(QWidget *parent = nullptr); void paintEvent(QPaintEvent *event) override; - public slots: void onBlit(const QImage& img, int, int, int, int); -private: - QImage image; - int sx, sy, sw, sh; +protected: + void resizeEvent(QResizeEvent *event) override; }; #endif // SOFTWARERENDERER_HPP