mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-04 00:36:36 +00:00
Refactor SDL overlay drawing into OverlayManager for sharing with other renderers
This commit is contained in:
parent
19d5358b97
commit
d58837421f
@ -2,54 +2,25 @@
|
|||||||
|
|
||||||
#include "streaming/session.h"
|
#include "streaming/session.h"
|
||||||
#include "streaming/streamutils.h"
|
#include "streaming/streamutils.h"
|
||||||
#include "path.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
|
|
||||||
SdlRenderer::SdlRenderer()
|
SdlRenderer::SdlRenderer()
|
||||||
: m_Renderer(nullptr),
|
: m_Renderer(nullptr),
|
||||||
m_Texture(nullptr),
|
m_Texture(nullptr),
|
||||||
m_SwPixelFormat(AV_PIX_FMT_NONE),
|
m_SwPixelFormat(AV_PIX_FMT_NONE)
|
||||||
m_FontData(Path::readDataFile("ModeSeven.ttf"))
|
|
||||||
{
|
{
|
||||||
SDL_assert(TTF_WasInit() == 0);
|
|
||||||
if (TTF_Init() != 0) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"TTF_Init() failed: %s",
|
|
||||||
TTF_GetError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_zero(m_OverlayFonts);
|
|
||||||
SDL_zero(m_OverlaySurfaces);
|
|
||||||
SDL_zero(m_OverlayTextures);
|
SDL_zero(m_OverlayTextures);
|
||||||
}
|
}
|
||||||
|
|
||||||
SdlRenderer::~SdlRenderer()
|
SdlRenderer::~SdlRenderer()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
|
||||||
if (m_OverlayFonts[i] != nullptr) {
|
|
||||||
TTF_CloseFont(m_OverlayFonts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TTF_Quit();
|
|
||||||
SDL_assert(TTF_WasInit() == 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
||||||
if (m_OverlayTextures[i] != nullptr) {
|
if (m_OverlayTextures[i] != nullptr) {
|
||||||
SDL_DestroyTexture(m_OverlayTextures[i]);
|
SDL_DestroyTexture(m_OverlayTextures[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
|
||||||
if (m_OverlaySurfaces[i] != nullptr) {
|
|
||||||
SDL_FreeSurface(m_OverlaySurfaces[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Texture != nullptr) {
|
if (m_Texture != nullptr) {
|
||||||
SDL_DestroyTexture(m_Texture);
|
SDL_DestroyTexture(m_Texture);
|
||||||
}
|
}
|
||||||
@ -69,47 +40,6 @@ bool SdlRenderer::prepareDecoderContext(AVCodecContext*, AVDictionary**)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
|
|
||||||
{
|
|
||||||
// Construct the required font to render the overlay
|
|
||||||
if (m_OverlayFonts[type] == nullptr) {
|
|
||||||
if (m_FontData.isEmpty()) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"SDL overlay font failed to load");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// m_FontData must stay around until the font is closed
|
|
||||||
m_OverlayFonts[type] = TTF_OpenFontRW(SDL_RWFromConstMem(m_FontData.constData(), m_FontData.size()),
|
|
||||||
1,
|
|
||||||
Session::get()->getOverlayManager().getOverlayFontSize(type));
|
|
||||||
if (m_OverlayFonts[type] == nullptr) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"TTF_OpenFont() failed: %s",
|
|
||||||
TTF_GetError());
|
|
||||||
|
|
||||||
// Can't proceed without a font
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
|
|
||||||
|
|
||||||
// Free the old surface
|
|
||||||
if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], oldSurface, nullptr)) {
|
|
||||||
SDL_FreeSurface(oldSurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
|
||||||
// The _Wrapped variant is required for line breaks to work
|
|
||||||
SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_OverlayFonts[type],
|
|
||||||
Session::get()->getOverlayManager().getOverlayText(type),
|
|
||||||
Session::get()->getOverlayManager().getOverlayColor(type),
|
|
||||||
1000);
|
|
||||||
SDL_AtomicSetPtr((void**)&m_OverlaySurfaces[type], surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SdlRenderer::isRenderThreadSupported()
|
bool SdlRenderer::isRenderThreadSupported()
|
||||||
{
|
{
|
||||||
SDL_RendererInfo info;
|
SDL_RendererInfo info;
|
||||||
@ -221,8 +151,8 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
|
|||||||
// If a new surface has been created for updated overlay data, convert it into a texture.
|
// If a new surface has been created for updated overlay data, convert it into a texture.
|
||||||
// NB: We have to do this conversion at render-time because we can only interact
|
// NB: We have to do this conversion at render-time because we can only interact
|
||||||
// with the renderer on a single thread.
|
// with the renderer on a single thread.
|
||||||
SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
|
SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
|
||||||
if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], surface, nullptr)) {
|
if (newSurface != nullptr) {
|
||||||
if (m_OverlayTextures[type] != nullptr) {
|
if (m_OverlayTextures[type] != nullptr) {
|
||||||
SDL_DestroyTexture(m_OverlayTextures[type]);
|
SDL_DestroyTexture(m_OverlayTextures[type]);
|
||||||
}
|
}
|
||||||
@ -232,7 +162,7 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
|
|||||||
SDL_Rect viewportRect;
|
SDL_Rect viewportRect;
|
||||||
SDL_RenderGetViewport(m_Renderer, &viewportRect);
|
SDL_RenderGetViewport(m_Renderer, &viewportRect);
|
||||||
m_OverlayRects[type].x = 0;
|
m_OverlayRects[type].x = 0;
|
||||||
m_OverlayRects[type].y = viewportRect.h - surface->h;
|
m_OverlayRects[type].y = viewportRect.h - newSurface->h;
|
||||||
}
|
}
|
||||||
else if (type == Overlay::OverlayDebug) {
|
else if (type == Overlay::OverlayDebug) {
|
||||||
// Top left
|
// Top left
|
||||||
@ -240,11 +170,11 @@ void SdlRenderer::renderOverlay(Overlay::OverlayType type)
|
|||||||
m_OverlayRects[type].y = 0;
|
m_OverlayRects[type].y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_OverlayRects[type].w = surface->w;
|
m_OverlayRects[type].w = newSurface->w;
|
||||||
m_OverlayRects[type].h = surface->h;
|
m_OverlayRects[type].h = newSurface->h;
|
||||||
|
|
||||||
m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, surface);
|
m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, newSurface);
|
||||||
SDL_FreeSurface(surface);
|
SDL_FreeSurface(newSurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have an overlay texture, render it too
|
// If we have an overlay texture, render it too
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
|
|
||||||
#include <SDL_ttf.h>
|
|
||||||
|
|
||||||
class SdlRenderer : public IFFmpegRenderer {
|
class SdlRenderer : public IFFmpegRenderer {
|
||||||
public:
|
public:
|
||||||
SdlRenderer();
|
SdlRenderer();
|
||||||
@ -11,7 +9,6 @@ public:
|
|||||||
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
||||||
virtual void renderFrame(AVFrame* frame) override;
|
virtual void renderFrame(AVFrame* frame) override;
|
||||||
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
|
||||||
virtual bool isRenderThreadSupported() override;
|
virtual bool isRenderThreadSupported() override;
|
||||||
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
|
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
|
||||||
|
|
||||||
@ -21,9 +18,6 @@ private:
|
|||||||
SDL_Renderer* m_Renderer;
|
SDL_Renderer* m_Renderer;
|
||||||
SDL_Texture* m_Texture;
|
SDL_Texture* m_Texture;
|
||||||
int m_SwPixelFormat;
|
int m_SwPixelFormat;
|
||||||
QByteArray m_FontData;
|
|
||||||
TTF_Font* m_OverlayFonts[Overlay::OverlayMax];
|
|
||||||
SDL_Surface* m_OverlaySurfaces[Overlay::OverlayMax];
|
|
||||||
SDL_Texture* m_OverlayTextures[Overlay::OverlayMax];
|
SDL_Texture* m_OverlayTextures[Overlay::OverlayMax];
|
||||||
SDL_Rect m_OverlayRects[Overlay::OverlayMax];
|
SDL_Rect m_OverlayRects[Overlay::OverlayMax];
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#include "overlaymanager.h"
|
#include "overlaymanager.h"
|
||||||
|
#include "path.h"
|
||||||
|
|
||||||
using namespace Overlay;
|
using namespace Overlay;
|
||||||
|
|
||||||
OverlayManager::OverlayManager() :
|
OverlayManager::OverlayManager() :
|
||||||
m_Renderer(nullptr)
|
m_Renderer(nullptr),
|
||||||
|
m_FontData(Path::readDataFile("ModeSeven.ttf"))
|
||||||
{
|
{
|
||||||
memset(m_Overlays, 0, sizeof(m_Overlays));
|
memset(m_Overlays, 0, sizeof(m_Overlays));
|
||||||
|
|
||||||
@ -12,6 +14,29 @@ OverlayManager::OverlayManager() :
|
|||||||
|
|
||||||
m_Overlays[OverlayType::OverlayStatusUpdate].color = {0xCC, 0x00, 0x00, 0xFF};
|
m_Overlays[OverlayType::OverlayStatusUpdate].color = {0xCC, 0x00, 0x00, 0xFF};
|
||||||
m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36;
|
m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36;
|
||||||
|
|
||||||
|
SDL_assert(TTF_WasInit() == 0);
|
||||||
|
if (TTF_Init() != 0) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"TTF_Init() failed: %s",
|
||||||
|
TTF_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OverlayManager::~OverlayManager()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < OverlayType::OverlayMax; i++) {
|
||||||
|
if (m_Overlays[i].surface != nullptr) {
|
||||||
|
SDL_FreeSurface(m_Overlays[i].surface);
|
||||||
|
}
|
||||||
|
if (m_Overlays[i].font != nullptr) {
|
||||||
|
TTF_CloseFont(m_Overlays[i].font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TTF_Quit();
|
||||||
|
SDL_assert(TTF_WasInit() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OverlayManager::isOverlayEnabled(OverlayType type)
|
bool OverlayManager::isOverlayEnabled(OverlayType type)
|
||||||
@ -29,12 +54,19 @@ int OverlayManager::getOverlayFontSize(OverlayType type)
|
|||||||
return m_Overlays[type].fontSize;
|
return m_Overlays[type].fontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_Surface* OverlayManager::getUpdatedOverlaySurface(OverlayType type)
|
||||||
|
{
|
||||||
|
// If a new surface is available, return it. If not, return nullptr.
|
||||||
|
// Caller must free the surface on success.
|
||||||
|
return (SDL_Surface*)SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayManager::setOverlayTextUpdated(OverlayType type)
|
void OverlayManager::setOverlayTextUpdated(OverlayType type)
|
||||||
{
|
{
|
||||||
// Only update the overlay state if it's enabled. If it's not enabled,
|
// Only update the overlay state if it's enabled. If it's not enabled,
|
||||||
// the renderer has already been notified by setOverlayState().
|
// the renderer has already been notified by setOverlayState().
|
||||||
if (m_Overlays[type].enabled && m_Renderer != nullptr) {
|
if (m_Overlays[type].enabled) {
|
||||||
m_Renderer->notifyOverlayUpdated(type);
|
notifyOverlayUpdated(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,9 +82,7 @@ void OverlayManager::setOverlayState(OverlayType type, bool enabled)
|
|||||||
m_Overlays[type].text[0] = 0;
|
m_Overlays[type].text[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Renderer != nullptr) {
|
notifyOverlayUpdated(type);
|
||||||
m_Renderer->notifyOverlayUpdated(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,3 +95,51 @@ void OverlayManager::setOverlayRenderer(IOverlayRenderer* renderer)
|
|||||||
{
|
{
|
||||||
m_Renderer = renderer;
|
m_Renderer = renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayManager::notifyOverlayUpdated(OverlayType type)
|
||||||
|
{
|
||||||
|
if (m_Renderer == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the required font to render the overlay
|
||||||
|
if (m_Overlays[type].font == nullptr) {
|
||||||
|
if (m_FontData.isEmpty()) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"SDL overlay font failed to load");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// m_FontData must stay around until the font is closed
|
||||||
|
m_Overlays[type].font = TTF_OpenFontRW(SDL_RWFromConstMem(m_FontData.constData(), m_FontData.size()),
|
||||||
|
1,
|
||||||
|
m_Overlays[type].fontSize);
|
||||||
|
if (m_Overlays[type].font == nullptr) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"TTF_OpenFont() failed: %s",
|
||||||
|
TTF_GetError());
|
||||||
|
|
||||||
|
// Can't proceed without a font
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, nullptr);
|
||||||
|
|
||||||
|
// Free the old surface
|
||||||
|
if (oldSurface != nullptr) {
|
||||||
|
SDL_FreeSurface(oldSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Overlays[type].enabled) {
|
||||||
|
// The _Wrapped variant is required for line breaks to work
|
||||||
|
SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_Overlays[type].font,
|
||||||
|
m_Overlays[type].text,
|
||||||
|
m_Overlays[type].color,
|
||||||
|
1000);
|
||||||
|
SDL_AtomicSetPtr((void**)&m_Overlays[type].surface, surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the renderer
|
||||||
|
m_Renderer->notifyOverlayUpdated(type);
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <SDL_ttf.h>
|
||||||
|
|
||||||
namespace Overlay {
|
namespace Overlay {
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ class OverlayManager
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OverlayManager();
|
OverlayManager();
|
||||||
|
~OverlayManager();
|
||||||
|
|
||||||
bool isOverlayEnabled(OverlayType type);
|
bool isOverlayEnabled(OverlayType type);
|
||||||
char* getOverlayText(OverlayType type);
|
char* getOverlayText(OverlayType type);
|
||||||
@ -31,16 +33,24 @@ public:
|
|||||||
void setOverlayState(OverlayType type, bool enabled);
|
void setOverlayState(OverlayType type, bool enabled);
|
||||||
SDL_Color getOverlayColor(OverlayType type);
|
SDL_Color getOverlayColor(OverlayType type);
|
||||||
int getOverlayFontSize(OverlayType type);
|
int getOverlayFontSize(OverlayType type);
|
||||||
|
SDL_Surface* getUpdatedOverlaySurface(OverlayType type);
|
||||||
|
|
||||||
void setOverlayRenderer(IOverlayRenderer* renderer);
|
void setOverlayRenderer(IOverlayRenderer* renderer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void notifyOverlayUpdated(OverlayType type);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int fontSize;
|
int fontSize;
|
||||||
SDL_Color color;
|
SDL_Color color;
|
||||||
char text[512];
|
char text[512];
|
||||||
|
|
||||||
|
TTF_Font* font;
|
||||||
|
SDL_Surface* surface;
|
||||||
} m_Overlays[OverlayMax];
|
} m_Overlays[OverlayMax];
|
||||||
IOverlayRenderer* m_Renderer;
|
IOverlayRenderer* m_Renderer;
|
||||||
|
QByteArray m_FontData;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user