diff --git a/CMakeLists.txt b/CMakeLists.txt index ac498d0d2..e86a761bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -738,6 +738,11 @@ elseif(BUILD_GLES2) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgles2") endif() +if(USE_EPOXY OR BUILD_GL OR BUILD_GLES2) + # This file should probably go somewhere else + list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/video-backend.c) +endif() + if(WIN32 AND NOT (LIBMGBA_ONLY OR SKIP_LIBRARY OR USE_EPOXY)) message(FATAL_ERROR "Windows requires epoxy module!") endif() diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index 8ad34197c..03bd831d2 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -9,9 +9,9 @@ static const GLint _glVertices[] = { 0, 0, - 256, 0, - 256, 256, - 0, 256 + 1, 0, + 1, 1, + 0, 1 }; static const GLint _glTexCoords[] = { @@ -101,15 +101,19 @@ static void mGLContextDeinit(struct VideoBackend* v) { } static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) { - struct mGLContext* context = (struct mGLContext*) v; unsigned drawW = w; unsigned drawH = h; + + unsigned maxW; + unsigned maxH; + VideoBackendGetFrameSize(v, &maxW, &maxH); + if (v->lockAspectRatio) { - lockAspectRatioUInt(context->layerDims[VIDEO_LAYER_IMAGE].width, context->layerDims[VIDEO_LAYER_IMAGE].height, &drawW, &drawH); + lockAspectRatioUInt(maxW, maxH, &drawW, &drawH); } if (v->lockIntegerScaling) { - lockIntegerRatioUInt(context->layerDims[VIDEO_LAYER_IMAGE].width, &drawW); - lockIntegerRatioUInt(context->layerDims[VIDEO_LAYER_IMAGE].height, &drawH); + lockIntegerRatioUInt(maxW, &drawW); + lockIntegerRatioUInt(maxH, &drawH); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -124,33 +128,7 @@ static void mGLContextClear(struct VideoBackend* v) { glClear(GL_COLOR_BUFFER_BIT); } -void mGLContextDrawFrame(struct VideoBackend* v) { - struct mGLContext* context = (struct mGLContext*) v; - glEnable(GL_TEXTURE_2D); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_INT, 0, _glVertices); - glTexCoordPointer(2, GL_INT, 0, _glTexCoords); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, context->layerDims[VIDEO_LAYER_IMAGE].width, context->layerDims[VIDEO_LAYER_IMAGE].height, 0, 0, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - if (v->interframeBlending) { - glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); - glBlendColor(1, 1, 1, 0.5); - glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]); - if (v->filter) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } else { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glEnable(GL_BLEND); - } - glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]); +static void _setFilter(struct VideoBackend* v) { if (v->filter) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -158,6 +136,61 @@ void mGLContextDrawFrame(struct VideoBackend* v) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } +} + +static void _setFrame(struct Rectangle* dims, int frameW, int frameH) { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + glScissor(viewport[0] + dims->x * viewport[2] / frameW, + viewport[1] + dims->y * viewport[3] / frameH, + dims->width * viewport[2] / frameW, + dims->height * viewport[3] / frameH); + glTranslatef(dims->x, dims->y, 0); + glScalef(toPow2(dims->width), toPow2(dims->height), 1); +} + +void mGLContextDrawFrame(struct VideoBackend* v) { + struct mGLContext* context = (struct mGLContext*) v; + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_INT, 0, _glVertices); + glTexCoordPointer(2, GL_INT, 0, _glTexCoords); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + unsigned frameW, frameH; + VideoBackendGetFrameSize(v, &frameW, &frameH); + glOrtho(0, frameW, frameH, 0, 0, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + int layer; + for (layer = 0; layer < VIDEO_LAYER_IMAGE; ++layer) { + if (context->layerDims[layer].width < 1 || context->layerDims[layer].height < 1) { + continue; + } + + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, context->layers[layer]); + _setFilter(v); + glPushMatrix(); + _setFrame(&context->layerDims[layer], frameW, frameH); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glPopMatrix(); + } + + _setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], frameW, frameH); + if (v->interframeBlending) { + glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + glBlendColor(1, 1, 1, 0.5); + glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]); + _setFilter(v); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glEnable(GL_BLEND); + } + glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]); + _setFilter(v); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisable(GL_BLEND); } diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index fb2a07bba..674955a77 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -240,12 +240,17 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) struct mGLES2Context* context = (struct mGLES2Context*) v; unsigned drawW = w; unsigned drawH = h; + + unsigned maxW; + unsigned maxH; + VideoBackendGetFrameSize(v, &maxW, &maxH); + if (v->lockAspectRatio) { - lockAspectRatioUInt(context->layerDims[VIDEO_LAYER_IMAGE].width, context->layerDims[VIDEO_LAYER_IMAGE].height, &drawW, &drawH); + lockAspectRatioUInt(maxW, maxH, &drawW, &drawH); } if (v->lockIntegerScaling) { - lockIntegerRatioUInt(context->layerDims[VIDEO_LAYER_IMAGE].width, &drawW); - lockIntegerRatioUInt(context->layerDims[VIDEO_LAYER_IMAGE].height, &drawH); + lockIntegerRatioUInt(maxW, &drawW); + lockIntegerRatioUInt(maxH, &drawH); } size_t n; for (n = 0; n < context->nShaders; ++n) { diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index fce25624d..e3155dc62 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -397,7 +397,7 @@ void DisplayGL::setVideoScale(int scale) { } void DisplayGL::setBackgroundImage(const QImage& image) { - // TODO + QMetaObject::invokeMethod(m_painter.get(), "setBackgroundImage", Q_ARG(const QImage&, image)); } void DisplayGL::resizeEvent(QResizeEvent* event) { @@ -631,12 +631,29 @@ void PainterGL::resizeContext() { Rectangle dims = {0, 0, size.width(), size.height()}; m_backend->setLayerDimensions(m_backend, VIDEO_LAYER_IMAGE, &dims); + recenterLayers(); } void PainterGL::setMessagePainter(MessagePainter* messagePainter) { m_messagePainter = messagePainter; } +void PainterGL::recenterLayers() { + const static std::initializer_list centeredLayers{VIDEO_LAYER_BACKGROUND, VIDEO_LAYER_IMAGE}; + Rectangle frame = {0}; + for (VideoLayer l : centeredLayers) { + Rectangle dims; + m_backend->layerDimensions(m_backend, l, &dims); + RectangleUnion(&frame, &dims); + } + for (VideoLayer l : centeredLayers) { + Rectangle dims; + m_backend->layerDimensions(m_backend, l, &dims); + RectangleCenter(&frame, &dims); + m_backend->setLayerDimensions(m_backend, l, &dims); + } +} + void PainterGL::resize(const QSize& size) { qreal r = m_window->devicePixelRatio(); m_size = size; @@ -995,6 +1012,32 @@ void PainterGL::updateFramebufferHandle() { m_context->setFramebufferHandle(m_bridgeTexIn); } +void PainterGL::setBackgroundImage(const QImage& image) { + if (!m_started) { + makeCurrent(); + } + + Rectangle dims = {0, 0, 0, 0}; + if (!image.isNull()) { + dims.width = static_cast(image.width()); + dims.height = static_cast(image.height()); + } + m_backend->setLayerDimensions(m_backend, VIDEO_LAYER_BACKGROUND, &dims); + recenterLayers(); + + if (!image.isNull()) { + m_background = image.convertToFormat(QImage::Format_RGB32); + m_background = m_background.rgbSwapped(); + m_backend->setImage(m_backend, VIDEO_LAYER_BACKGROUND, m_background.constBits()); + } else { + m_background = QImage(); + } + + if (!m_started) { + m_gl->doneCurrent(); + } +} + void PainterGL::swapTex() { if (!m_started) { return; diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 87dd983dd..eed3a37c0 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -109,7 +109,6 @@ public slots: void setVideoScale(int scale) override; void setBackgroundImage(const QImage&) override; - protected: virtual void paintEvent(QPaintEvent*) override { forceDraw(); } virtual void resizeEvent(QResizeEvent*) override; @@ -178,6 +177,7 @@ public slots: void filter(bool filter); void resizeContext(); void updateFramebufferHandle(); + void setBackgroundImage(const QImage&); void setShaders(struct VDir*); void clearShaders(); @@ -196,6 +196,7 @@ private: void performDraw(); void dequeue(); void dequeueAll(bool keep = false); + void recenterLayers(); std::array, 3> m_buffers; QList m_free; @@ -214,6 +215,7 @@ private: QWindow* m_window; QSurface* m_surface; QSurfaceFormat m_format; + QImage m_background; std::unique_ptr m_paintDev; std::unique_ptr m_gl; int m_finalTexIdx = 0; diff --git a/src/platform/video-backend.c b/src/platform/video-backend.c new file mode 100644 index 000000000..3447fcdd8 --- /dev/null +++ b/src/platform/video-backend.c @@ -0,0 +1,22 @@ +/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "video-backend.h" + +void VideoBackendGetFrameSize(const struct VideoBackend* v, unsigned* width, unsigned* height) { + *width = 0; + *height = 0; + int i; + for (i = 0; i < VIDEO_LAYER_MAX; ++i) { + struct Rectangle dims; + v->layerDimensions(v, i, &dims); + if (dims.x + dims.width > *width) { + *width = dims.x + dims.width; + } + if (dims.y + dims.height > *height) { + *height = dims.y + dims.height; + } + } +} diff --git a/src/platform/video-backend.h b/src/platform/video-backend.h index 3b7a4b245..3bb1c0a69 100644 --- a/src/platform/video-backend.h +++ b/src/platform/video-backend.h @@ -21,6 +21,7 @@ typedef void* WHandle; enum VideoLayer { VIDEO_LAYER_BACKGROUND = 0, + VIDEO_LAYER_BEZEL, VIDEO_LAYER_IMAGE, VIDEO_LAYER_OVERLAY, VIDEO_LAYER_MAX @@ -55,6 +56,8 @@ struct VideoShader { size_t nPasses; }; +void VideoBackendGetFrameSize(const struct VideoBackend*, unsigned* width, unsigned* height); + CXX_GUARD_END #endif