Qt: Expose DisplayQt as a VideoBackend

This commit is contained in:
Vicki Pfau 2023-05-01 02:45:43 -07:00
parent dda5634189
commit 428a29dae3
5 changed files with 175 additions and 96 deletions

View File

@ -10,6 +10,7 @@
#include "DisplayGL.h"
#include "DisplayQt.h"
#include "LogController.h"
#include "VideoProxy.h"
#include "utils.h"
#include <mgba-util/vfs.h>
@ -124,6 +125,13 @@ void QGBA::Display::configure(ConfigController* config) {
#endif
}
VideoBackend* QGBA::Display::videoBackend() {
if (m_videoProxy) {
return m_videoProxy->backend();
}
return nullptr;
}
void QGBA::Display::resizeEvent(QResizeEvent*) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
m_messagePainter.resize(size(), devicePixelRatioF());

View File

@ -60,6 +60,7 @@ public:
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = proxy; }
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }
virtual VideoBackend* videoBackend();
signals:
void drawingStarted();

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -19,13 +19,28 @@ using namespace QGBA;
DisplayQt::DisplayQt(QWidget* parent)
: Display(parent)
{
m_backend.init = &DisplayQt::init;
m_backend.deinit = &DisplayQt::deinit;
m_backend.setLayerDimensions = &DisplayQt::setLayerDimensions;
m_backend.layerDimensions = &DisplayQt::layerDimensions;
m_backend.swap = &DisplayQt::swap;
m_backend.clear = &DisplayQt::clear;
m_backend.contextResized = &DisplayQt::contextResized;
m_backend.setImageSize = &DisplayQt::setImageSize;
m_backend.imageSize = &DisplayQt::imageSize;
m_backend.setImage = &DisplayQt::setImage;
m_backend.drawFrame = &DisplayQt::drawFrame;
m_backend.filter = isFiltered();
m_backend.lockAspectRatio = isAspectRatioLocked();
m_backend.lockIntegerScaling = isIntegerScalingLocked();
m_backend.interframeBlending = hasInterframeBlending();
m_backend.user = this;
}
void DisplayQt::startDrawing(std::shared_ptr<CoreController> controller) {
QSize size = controller->screenDimensions();
m_width = size.width();
m_height = size.height();
m_backing = QImage();
m_oldBacking = QImage();
m_isDrawing = true;
m_context = controller;
@ -39,44 +54,51 @@ void DisplayQt::stopDrawing() {
void DisplayQt::lockAspectRatio(bool lock) {
Display::lockAspectRatio(lock);
m_backend.lockAspectRatio = lock;
update();
}
void DisplayQt::lockIntegerScaling(bool lock) {
Display::lockIntegerScaling(lock);
m_backend.lockIntegerScaling = lock;
update();
}
void DisplayQt::interframeBlending(bool lock) {
Display::interframeBlending(lock);
m_backend.interframeBlending = lock;
update();
}
void DisplayQt::filter(bool filter) {
Display::filter(filter);
m_backend.filter = filter;
update();
}
void DisplayQt::framePosted() {
update();
const color_t* buffer = m_context->drawContext();
if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) {
if (const_cast<const QImage&>(m_layers[VIDEO_LAYER_IMAGE]).bits() == reinterpret_cast<const uchar*>(buffer)) {
return;
}
m_oldBacking = m_backing;
m_oldBacking = m_layers[VIDEO_LAYER_IMAGE];
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB16);
m_layers[VIDEO_LAYER_IMAGE] = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB16);
#else
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB555);
m_layers[VIDEO_LAYER_IMAGE] = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB555);
#endif
#else
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32);
m_backing = m_backing.convertToFormat(QImage::Format_RGB32);
m_layers[VIDEO_LAYER_IMAGE] = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32);
m_layers[VIDEO_LAYER_IMAGE] = m_layers[VIDEO_LAYER_IMAGE].convertToFormat(QImage::Format_RGB32);
#endif
#ifndef COLOR_5_6_5
m_backing = m_backing.rgbSwapped();
m_layers[VIDEO_LAYER_IMAGE] = m_layers[VIDEO_LAYER_IMAGE].rgbSwapped();
#endif
m_layerDims[VIDEO_LAYER_IMAGE].setWidth(m_width);
m_layerDims[VIDEO_LAYER_IMAGE].setHeight(m_height);
redoBounds();
}
void DisplayQt::resizeContext() {
@ -88,12 +110,13 @@ void DisplayQt::resizeContext() {
m_width = size.width();
m_height = size.height();
m_oldBacking = QImage();
m_backing = QImage();
m_layers[VIDEO_LAYER_IMAGE] = QImage();
}
}
void DisplayQt::setBackgroundImage(const QImage& image) {
m_background = image;
m_layers[VIDEO_LAYER_BACKGROUND] = image;
redoBounds();
update();
}
@ -104,91 +127,125 @@ void DisplayQt::paintEvent(QPaintEvent*) {
painter.setRenderHint(QPainter::SmoothPixmapTransform);
}
QRect bgRect(0, 0, m_background.width(), m_background.height());
QRect imRect(0, 0, m_width, m_height);
QSize outerFrame = contentSize();
struct mRectangle frame;
VideoBackendGetFrame(&m_backend, &frame);
QPoint origin(-frame.x, -frame.y);
QRect full(clampSize(contentSize(), size(), isAspectRatioLocked(), isIntegerScalingLocked()));
painter.save();
painter.translate(full.topLeft());
painter.scale(full.width() / static_cast<qreal>(frame.width), full.height() / static_cast<qreal>(frame.height));
if (bgRect.width() > imRect.width()) {
imRect.moveLeft(bgRect.width() - imRect.width());
} else {
bgRect.moveLeft(imRect.width() - bgRect.width());
}
if (bgRect.height() > imRect.height()) {
imRect.moveTop(bgRect.height() - imRect.height());
} else {
bgRect.moveTop(imRect.height() - bgRect.height());
}
QRect full(clampSize(outerFrame, size(), isAspectRatioLocked(), isIntegerScalingLocked()));
if (m_background.isNull()) {
imRect = full;
} else {
if (imRect.x()) {
imRect.moveLeft(imRect.x() * full.width() / bgRect.width() / 2);
imRect.setWidth(imRect.width() * full.width() / bgRect.width());
bgRect.setWidth(full.width());
} else {
bgRect.moveLeft(bgRect.x() * full.width() / imRect.width() / 2);
bgRect.setWidth(bgRect.width() * full.width() / imRect.width());
imRect.setWidth(full.width());
}
if (imRect.y()) {
imRect.moveTop(imRect.y() * full.height() / bgRect.height() / 2);
imRect.setHeight(imRect.height() * full.height() / bgRect.height());
bgRect.setHeight(full.height());
} else {
bgRect.moveTop(bgRect.y() * full.height() / imRect.height() / 2);
bgRect.setHeight(bgRect.height() * full.height() / imRect.height());
imRect.setHeight(full.height());
}
if (bgRect.right() > imRect.right()) {
if (bgRect.right() < full.right()) {
imRect.translate((full.right() - bgRect.right()), 0);
bgRect.translate((full.right() - bgRect.right()), 0);
}
} else {
if (imRect.right() < full.right()) {
bgRect.translate((full.right() - imRect.right()), 0);
imRect.translate((full.right() - imRect.right()), 0);
}
}
if (bgRect.bottom() > imRect.bottom()) {
if (bgRect.bottom() < full.bottom()) {
imRect.translate(0, (full.bottom() - bgRect.bottom()));
bgRect.translate(0, (full.bottom() - bgRect.bottom()));
}
} else {
if (imRect.bottom() < full.bottom()) {
bgRect.translate(0, (full.bottom() - imRect.bottom()));
imRect.translate(0, (full.bottom() - imRect.bottom()));
}
}
painter.drawImage(bgRect, m_background);
if (!m_layers[VIDEO_LAYER_BACKGROUND].isNull()) {
painter.drawImage(m_layerDims[VIDEO_LAYER_BACKGROUND].translated(origin), m_layers[VIDEO_LAYER_BACKGROUND]);
}
if (hasInterframeBlending()) {
painter.drawImage(imRect, m_oldBacking, QRect(0, 0, m_width, m_height));
painter.drawImage(m_layerDims[VIDEO_LAYER_IMAGE].translated(origin), m_oldBacking, QRect(0, 0, m_width, m_height));
painter.setOpacity(0.5);
}
painter.drawImage(imRect, m_backing, QRect(0, 0, m_width, m_height));
painter.drawImage(m_layerDims[VIDEO_LAYER_IMAGE].translated(origin), m_layers[VIDEO_LAYER_IMAGE], QRect(0, 0, m_width, m_height));
for (int i = VIDEO_LAYER_IMAGE + 1; i < VIDEO_LAYER_MAX; ++i) {
if (m_layers[i].isNull()) {
continue;
}
painter.drawImage(m_layerDims[i].translated(origin), m_layers[i]);
}
painter.restore();
painter.setOpacity(1);
if (isShowOSD() || isShowFrameCounter()) {
messagePainter()->paint(&painter);
}
}
QSize DisplayQt::contentSize() const {
QSize outerFrame(m_width, m_height);
void DisplayQt::redoBounds() {
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND};
mRectangle frame = {0};
frame.width = m_width;
frame.height = m_height;
if (m_background.width() > outerFrame.width()) {
outerFrame.setWidth(m_background.width());
for (VideoLayer l : centeredLayers) {
mRectangle dims{};
dims.width = m_layers[l].width();
dims.height = m_layers[l].height();
mRectangleCenter(&frame, &dims);
m_layerDims[l].setX(dims.x);
m_layerDims[l].setY(dims.y);
m_layerDims[l].setWidth(dims.width);
m_layerDims[l].setHeight(dims.height);
}
if (m_background.height() > outerFrame.height()) {
outerFrame.setHeight(m_background.height());
}
return outerFrame;
}
QSize DisplayQt::contentSize() const {
unsigned w, h;
VideoBackendGetFrameSize(&m_backend, &w, &h);
return {w, h};
}
void DisplayQt::init(struct VideoBackend*, WHandle) {
}
void DisplayQt::deinit(struct VideoBackend*) {
}
void DisplayQt::setLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct mRectangle* dims) {
DisplayQt* self = static_cast<DisplayQt*>(v->user);
if (layer > self->m_layerDims.size()) {
return;
}
self->m_layerDims[layer] = QRect(dims->x, dims->y, dims->width, dims->height);
}
void DisplayQt::layerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct mRectangle* dims) {
DisplayQt* self = static_cast<DisplayQt*>(v->user);
if (layer > self->m_layerDims.size()) {
return;
}
QRect rect = self->m_layerDims[layer];
dims->x = rect.x();
dims->y = rect.y();
dims->width = rect.width();
dims->height = rect.height();
}
void DisplayQt::swap(struct VideoBackend*) {
}
void DisplayQt::clear(struct VideoBackend*) {
}
void DisplayQt::contextResized(struct VideoBackend*, unsigned, unsigned) {
}
void DisplayQt::setImageSize(struct VideoBackend* v, enum VideoLayer layer, int w, int h) {
DisplayQt* self = static_cast<DisplayQt*>(v->user);
if (layer > self->m_layers.size()) {
return;
}
self->m_layers[layer] = QImage(w, h, QImage::Format_ARGB32);
}
void DisplayQt::imageSize(struct VideoBackend* v, enum VideoLayer layer, int* w, int* h) {
DisplayQt* self = static_cast<DisplayQt*>(v->user);
if (layer > self->m_layers.size()) {
return;
}
*w = self->m_layers[layer].width();
*h = self->m_layers[layer].height();
}
void DisplayQt::setImage(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
DisplayQt* self = static_cast<DisplayQt*>(v->user);
if (layer > self->m_layers.size()) {
return;
}
QImage image = self->m_layers[layer];
image = QImage(static_cast<const uchar*>(frame), image.width(), image.height(), QImage::Format_ARGB32).rgbSwapped();
self->m_layers[layer] = image;
}
void DisplayQt::drawFrame(struct VideoBackend* v) {
QMetaObject::invokeMethod(static_cast<DisplayQt*>(v->user), "update");
}

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -10,6 +10,9 @@
#include <QImage>
#include <QTimer>
#include <mgba/feature/video-backend.h>
#include <array>
namespace QGBA {
class DisplayQt : public Display {
@ -23,6 +26,7 @@ public:
bool supportsShaders() const override { return false; }
VideoShader* shaders() override { return nullptr; }
QSize contentSize() const override;
VideoBackend* videoBackend() override { return &m_backend; }
public slots:
void stopDrawing() override;
@ -44,12 +48,27 @@ protected:
virtual void paintEvent(QPaintEvent*) override;
private:
void redoBounds();
static void init(struct VideoBackend*, WHandle);
static void deinit(struct VideoBackend*);
static void setLayerDimensions(struct VideoBackend*, enum VideoLayer, const struct mRectangle*);
static void layerDimensions(const struct VideoBackend*, enum VideoLayer, struct mRectangle*);
static void swap(struct VideoBackend*);
static void clear(struct VideoBackend*);
static void contextResized(struct VideoBackend*, unsigned w, unsigned h);
static void setImageSize(struct VideoBackend*, enum VideoLayer, int w, int h);
static void imageSize(struct VideoBackend*, enum VideoLayer, int* w, int* h);
static void setImage(struct VideoBackend*, enum VideoLayer, const void* frame);
static void drawFrame(struct VideoBackend*);
VideoBackend m_backend{};
std::array<QRect, VIDEO_LAYER_MAX> m_layerDims;
std::array<QImage, VIDEO_LAYER_MAX> m_layers;
bool m_isDrawing = false;
int m_width = -1;
int m_height = -1;
QImage m_backing{nullptr};
QImage m_oldBacking{nullptr};
QImage m_background;
std::shared_ptr<CoreController> m_context = nullptr;
};

View File

@ -634,10 +634,7 @@ void Window::scriptingOpen() {
m_display->installEventFilter(m_scripting.get());
}
std::shared_ptr<VideoProxy> proxy = m_display->videoProxy();
if (proxy) {
m_scripting->setVideoBackend(proxy->backend());
}
m_scripting->setVideoBackend(m_display->videoBackend());
}
ScriptingView* view = new ScriptingView(m_scripting.get(), m_config);
openView(view);
@ -1082,7 +1079,7 @@ void Window::reloadDisplayDriver() {
m_display->setVideoProxy(proxy);
#ifdef ENABLE_SCRIPTING
if (m_scripting) {
m_scripting->setVideoBackend(proxy->backend());
m_scripting->setVideoBackend(m_display->videoBackend());
}
#endif
}
@ -2135,10 +2132,7 @@ void Window::setController(CoreController* controller, const QString& fname) {
if (m_scripting) {
m_scripting->setController(m_controller);
std::shared_ptr<VideoProxy> proxy = m_display->videoProxy();
if (proxy) {
m_scripting->setVideoBackend(proxy->backend());
}
m_scripting->setVideoBackend(m_display->videoBackend());
}
#endif