mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 06:14:08 +08:00
Let UI render on the physical screen rather than the logical screen
This commit is contained in:
@@ -30,7 +30,7 @@ bool GameApplication::initialize() {
|
|||||||
// 字体管理
|
// 字体管理
|
||||||
m_fontManager = std::make_unique<FontManager>();
|
m_fontManager = std::make_unique<FontManager>();
|
||||||
// 文字渲染
|
// 文字渲染
|
||||||
m_textRenderer = std::make_unique<TextRenderer>(m_windowManager->GetRenderer(), m_fontManager.get());
|
m_textRenderer = std::make_unique<TextRenderer>(m_windowManager->GetRenderer(), m_fontManager.get(), m_windowManager->getViewport());
|
||||||
|
|
||||||
m_uiRenderer = std:: make_unique<UIRenderer>(m_windowManager->GetRenderer(), m_textRenderer.get());
|
m_uiRenderer = std:: make_unique<UIRenderer>(m_windowManager->GetRenderer(), m_textRenderer.get());
|
||||||
|
|
||||||
@@ -54,12 +54,27 @@ SDL_AppResult GameApplication::handleInputEvent(SDL_Event* event) {
|
|||||||
}
|
}
|
||||||
m_sceneManager->handleMousePosition(input.mouseCurrentPosition);
|
m_sceneManager->handleMousePosition(input.mouseCurrentPosition);
|
||||||
m_windowManager->setFullscreen(input.isFullscreen);
|
m_windowManager->setFullscreen(input.isFullscreen);
|
||||||
|
// 改变窗口时清理旧的缓存
|
||||||
|
if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
|
||||||
|
m_textRenderer->clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameApplication::run() {
|
void GameApplication::run() {
|
||||||
m_windowManager->Clear();
|
|
||||||
m_sceneManager->updateCurrent();
|
m_sceneManager->updateCurrent();
|
||||||
m_sceneManager->renderCurrent();
|
|
||||||
|
m_windowManager->Clear();
|
||||||
|
m_windowManager->beginWorld();
|
||||||
|
m_sceneManager->renderWorld();
|
||||||
|
m_windowManager->endWorld();
|
||||||
|
|
||||||
|
m_windowManager->beginUI();
|
||||||
|
m_sceneManager->renderUI();
|
||||||
|
m_windowManager->endUI();
|
||||||
|
|
||||||
m_windowManager->Present();
|
m_windowManager->Present();
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ private:
|
|||||||
std::unique_ptr<TextRenderer> m_textRenderer;
|
std::unique_ptr<TextRenderer> m_textRenderer;
|
||||||
std::unique_ptr<UIRenderer> m_uiRenderer;
|
std::unique_ptr<UIRenderer> m_uiRenderer;
|
||||||
|
|
||||||
GameConfig m_config;
|
WindowConfig m_config;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameApplication();
|
GameApplication();
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ WindowManager::~WindowManager() {
|
|||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowManager::Initialize(GameConfig& config) {
|
bool WindowManager::Initialize(WindowConfig& config) {
|
||||||
m_logicalWidth = config.logicalWidth;
|
m_viewport.logicalWidth = config.logicalWidth;
|
||||||
m_logicalHeight = config.logicalHeight;
|
m_viewport.logicalHeight = config.logicalHeight;
|
||||||
// 创建窗口(支持高DPI和横屏)[3,4](@ref)
|
// 创建窗口(支持高DPI和横屏)[3,4](@ref)
|
||||||
m_window = SDL_CreateWindow(
|
m_window = SDL_CreateWindow(
|
||||||
"孢子棋", // 窗口标题,显示在标题栏上
|
"孢子棋", // 窗口标题,显示在标题栏上
|
||||||
m_logicalWidth, // 窗口的逻辑宽度(例如 800),用于统一布局,不受屏幕 DPI 影响
|
m_viewport.logicalWidth, // 窗口的逻辑宽度(例如 800),用于统一布局,不受屏幕 DPI 影响
|
||||||
m_logicalHeight, // 窗口的逻辑高度(例如 600)
|
m_viewport.logicalHeight, // 窗口的逻辑高度(例如 600)
|
||||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | // 启用高像素密度支持(HiDPI/Retina),确保在高分屏上画面清晰
|
SDL_WINDOW_HIGH_PIXEL_DENSITY | // 启用高像素密度支持(HiDPI/Retina),确保在高分屏上画面清晰
|
||||||
SDL_WINDOW_RESIZABLE // 允许用户调整窗口大小(可拉伸)
|
SDL_WINDOW_RESIZABLE // 允许用户调整窗口大小(可拉伸)
|
||||||
);
|
);
|
||||||
@@ -52,8 +52,8 @@ bool WindowManager::Initialize(GameConfig& config) {
|
|||||||
m_renderer,
|
m_renderer,
|
||||||
SDL_PIXELFORMAT_RGBA8888,
|
SDL_PIXELFORMAT_RGBA8888,
|
||||||
SDL_TEXTUREACCESS_TARGET,
|
SDL_TEXTUREACCESS_TARGET,
|
||||||
m_logicalWidth,
|
m_viewport.logicalWidth,
|
||||||
m_logicalHeight
|
m_viewport.logicalHeight
|
||||||
);
|
);
|
||||||
// 设置纹理缩放模式为最近邻
|
// 设置纹理缩放模式为最近邻
|
||||||
SDL_SetTextureScaleMode(m_logicalTexture, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(m_logicalTexture, SDL_SCALEMODE_NEAREST);
|
||||||
@@ -67,13 +67,12 @@ void WindowManager::Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::Clear() {
|
void WindowManager::Clear() {
|
||||||
// 1. 设置渲染目标为逻辑纹理
|
|
||||||
SDL_SetRenderTarget(m_renderer, m_logicalTexture);
|
|
||||||
// 设置画笔颜色
|
// 设置画笔颜色
|
||||||
SDL_SetRenderDrawColor(m_renderer, 255, 255, 255, 255);
|
SDL_SetRenderDrawColor(m_renderer, 255, 255, 255, 255);
|
||||||
// 使用画笔颜色填充整个逻辑画布
|
// 使用画笔颜色填充整个画布
|
||||||
SDL_RenderClear(m_renderer);
|
SDL_RenderClear(m_renderer);
|
||||||
|
onWindowResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -89,21 +88,40 @@ void WindowManager::Clear() {
|
|||||||
|
|
||||||
void WindowManager::Present() {
|
void WindowManager::Present() {
|
||||||
|
|
||||||
// 4. 切回默认渲染目标(窗口)
|
|
||||||
SDL_SetRenderTarget(m_renderer, nullptr);
|
|
||||||
SDL_RenderClear(m_renderer); // 清的是窗口(黑边
|
//SDL_FRect dstRect;
|
||||||
SDL_FRect dstRect;
|
//calculateDstRect(dstRect);
|
||||||
calculateDstRect(dstRect);
|
|
||||||
SDL_RenderTexture (
|
SDL_RenderTexture (
|
||||||
m_renderer,
|
m_renderer,
|
||||||
m_logicalTexture, // 源:你已经画好的逻辑画布
|
m_logicalTexture, // 源:你已经画好的逻辑画布
|
||||||
nullptr, // srcRect:源区域(nullptr = 整张)
|
nullptr, // srcRect:源区域(nullptr = 整张)
|
||||||
&dstRect // dstRect:贴到哪里 & 贴多大
|
&m_viewport.dst // dstRect:贴到哪里 & 贴多大
|
||||||
);
|
);
|
||||||
SDL_RenderPresent(m_renderer);
|
SDL_RenderPresent(m_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WindowManager::beginWorld() {
|
||||||
|
// 设置渲染目标为逻辑纹理
|
||||||
|
SDL_SetRenderTarget(m_renderer, m_logicalTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::endWorld() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::beginUI() {
|
||||||
|
// 设置渲染目标为窗口
|
||||||
|
SDL_SetRenderTarget(m_renderer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::endUI() {
|
||||||
|
// 恢复渲染目标为默认(窗口)
|
||||||
|
SDL_SetRenderTarget(m_renderer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Renderer* WindowManager::GetRenderer() {
|
SDL_Renderer* WindowManager::GetRenderer() {
|
||||||
return m_renderer;
|
return m_renderer;
|
||||||
}
|
}
|
||||||
@@ -121,7 +139,7 @@ bool WindowManager::setFullscreen(bool isFullscreen) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
void WindowManager::calculateDstRect(SDL_FRect& dstRect) {
|
void WindowManager::calculateDstRect(SDL_FRect& dstRect) {
|
||||||
// 获取窗口的实际尺寸(像素)
|
// 获取窗口的实际尺寸(像素)
|
||||||
SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight);
|
SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight);
|
||||||
@@ -136,4 +154,59 @@ void WindowManager::calculateDstRect(SDL_FRect& dstRect) {
|
|||||||
// 居中显示
|
// 居中显示
|
||||||
dstRect.x = static_cast<float>((m_windowWidth - dstRect.w) / 2);
|
dstRect.x = static_cast<float>((m_windowWidth - dstRect.w) / 2);
|
||||||
dstRect.y = static_cast<float>((m_windowHeight - dstRect.h) / 2);
|
dstRect.y = static_cast<float>((m_windowHeight - dstRect.h) / 2);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
void WindowManager::onWindowResize() {
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// 1. 查询窗口的真实像素尺寸
|
||||||
|
// 注意:这是“窗口坐标空间”的大小
|
||||||
|
// =================================================
|
||||||
|
SDL_GetWindowSize(
|
||||||
|
m_window,
|
||||||
|
&m_viewport.windowWidth,
|
||||||
|
&m_viewport.windowHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// 2. 计算 X / Y 方向最多能放大多少倍
|
||||||
|
// 使用整数除法,保证整数缩放(像素不模糊)
|
||||||
|
// =================================================
|
||||||
|
int scaleX = m_viewport.windowWidth / m_viewport.logicalWidth;
|
||||||
|
int scaleY = m_viewport.windowHeight / m_viewport.logicalHeight;
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// 3. 取较小值,保证逻辑画面完整显示
|
||||||
|
// 同时确保最小为 1(窗口再小也能画)
|
||||||
|
// =================================================
|
||||||
|
m_viewport.scale = std::max(1, std::min(scaleX, scaleY));
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// 4. 计算逻辑画面放大后的实际尺寸
|
||||||
|
// =================================================
|
||||||
|
m_viewport.dst.w =
|
||||||
|
static_cast<float>(m_viewport.logicalWidth *
|
||||||
|
m_viewport.scale);
|
||||||
|
|
||||||
|
m_viewport.dst.h =
|
||||||
|
static_cast<float>(m_viewport.logicalHeight *
|
||||||
|
m_viewport.scale);
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// 5. 计算居中偏移(letterbox / pillarbox)
|
||||||
|
// =================================================
|
||||||
|
m_viewport.dst.x =
|
||||||
|
(m_viewport.windowWidth - m_viewport.dst.w) * 0.5f;
|
||||||
|
|
||||||
|
m_viewport.dst.y =
|
||||||
|
(m_viewport.windowHeight - m_viewport.dst.h) * 0.5f;
|
||||||
|
|
||||||
|
// 到这里为止:
|
||||||
|
// - scale 可供所有系统使用
|
||||||
|
// - dst 是渲染 & 输入转换的唯一依据
|
||||||
|
// - windowSize 不需要再到处 SDL_GetWindowSize
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewport const& WindowManager::getViewport() const {
|
||||||
|
return m_viewport;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,16 +6,26 @@ public:
|
|||||||
WindowManager();
|
WindowManager();
|
||||||
~WindowManager();
|
~WindowManager();
|
||||||
// 初始化
|
// 初始化
|
||||||
bool Initialize(GameConfig& config);
|
bool Initialize(WindowConfig& config);
|
||||||
//关闭
|
//关闭
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
//清理上一帧窗口
|
//清理上一帧窗口
|
||||||
|
|
||||||
|
void beginWorld();
|
||||||
|
|
||||||
|
void endWorld();
|
||||||
|
|
||||||
|
void beginUI();
|
||||||
|
|
||||||
|
void endUI();
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
//呈现窗口
|
//呈现窗口
|
||||||
void Present();
|
void Present();
|
||||||
bool setFullscreen(bool isFullscreen);
|
bool setFullscreen(bool isFullscreen);
|
||||||
|
|
||||||
|
const Viewport& getViewport() const;
|
||||||
|
|
||||||
SDL_Renderer* GetRenderer();
|
SDL_Renderer* GetRenderer();
|
||||||
SDL_Window* GetWindow();
|
SDL_Window* GetWindow();
|
||||||
|
|
||||||
@@ -23,13 +33,15 @@ public:
|
|||||||
private:
|
private:
|
||||||
SDL_Window* m_window;
|
SDL_Window* m_window;
|
||||||
SDL_Renderer* m_renderer;
|
SDL_Renderer* m_renderer;
|
||||||
int m_logicalWidth;
|
Viewport m_viewport;
|
||||||
int m_logicalHeight;
|
//int m_logicalWidth;
|
||||||
int m_windowWidth;
|
//int m_logicalHeight;
|
||||||
int m_windowHeight;
|
//int m_windowWidth;
|
||||||
|
//int m_windowHeight;
|
||||||
bool m_isFullscreen = false;
|
bool m_isFullscreen = false;
|
||||||
SDL_Texture* m_logicalTexture = nullptr;
|
SDL_Texture* m_logicalTexture = nullptr;
|
||||||
// 计算缩放后的目标矩形
|
// 计算缩放后的目标矩形
|
||||||
void calculateDstRect(SDL_FRect& dstRect);
|
//void calculateDstRect(SDL_FRect& dstRect);
|
||||||
|
void onWindowResize();
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "TextRenderer.h"
|
#include "TextRenderer.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
TextRenderer::TextRenderer(SDL_Renderer* renderer, FontManager* fontManager) :
|
#include <iostream>
|
||||||
|
TextRenderer::TextRenderer(SDL_Renderer* renderer, FontManager* fontManager, const Viewport& viewport) :
|
||||||
m_fontManager(fontManager),
|
m_fontManager(fontManager),
|
||||||
m_renderer(renderer)
|
m_renderer(renderer),
|
||||||
|
m_viewport(viewport)
|
||||||
{
|
{
|
||||||
//m_bitmapFont = std::make_unique<BitmapFont>();
|
//m_bitmapFont = std::make_unique<BitmapFont>();
|
||||||
//m_bitmapFont->load("assets/fonts/sanhan.fnt", renderer);
|
//m_bitmapFont->load("assets/fonts/sanhan.fnt", renderer);
|
||||||
@@ -13,7 +15,7 @@ TextRenderer::~TextRenderer() {
|
|||||||
clearCache();
|
clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int> TextRenderer::getTextSize(const std::string& text, TextStyle style) {
|
std::pair<int, int> TextRenderer::getLogicalTextSize(const std::string& text, TextStyle style) {
|
||||||
auto key = makeHash(text, style);
|
auto key = makeHash(text, style);
|
||||||
auto it = m_cache.find(key);
|
auto it = m_cache.find(key);
|
||||||
|
|
||||||
@@ -29,46 +31,60 @@ std::pair<int, int> TextRenderer::getTextSize(const std::string& text, TextStyle
|
|||||||
if (!cached.texture) {
|
if (!cached.texture) {
|
||||||
return {0, 0};
|
return {0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {cached.width, cached.height};
|
return {cached.width / m_viewport.scale, cached.height / m_viewport.scale};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextRenderer::renderText(const std::string& text, TextStyle style, int x, int y) {
|
void TextRenderer::renderText(const std::string& text, TextStyle style, int logicalX, int logicalY) {
|
||||||
auto key = makeHash(text, style);
|
|
||||||
auto it = m_cache.find(key);
|
|
||||||
|
|
||||||
// 查找缓存
|
CachedText& cached = createAndCacheTexture(text, style);
|
||||||
if (it != m_cache.end()) {
|
|
||||||
// 更新最后访问时间
|
|
||||||
it->second.lastAccessTime = std::time(nullptr);
|
|
||||||
|
|
||||||
// 使用缓存的纹理, SDL_FRect为浮点数矩形
|
|
||||||
SDL_FRect dest = { static_cast<float>(x), static_cast<float>(y),
|
|
||||||
static_cast<float>(it->second.width),
|
|
||||||
static_cast<float>(it->second.height) };
|
|
||||||
|
|
||||||
// 绘制材质 NULL 的含义:绘制整个纹理(从 (0,0) 到纹理的完整宽高) &dest 目标区域
|
|
||||||
SDL_RenderTexture(m_renderer, it->second.texture, NULL, &dest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建并缓存纹理
|
|
||||||
CachedText cached = createAndCacheTexture(text, style);
|
|
||||||
if (!cached.texture) {
|
if (!cached.texture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// 1. 逻辑坐标 → 窗口坐标
|
||||||
|
// ===============================
|
||||||
|
SDL_FPoint winPos = {
|
||||||
|
m_viewport.dst.x + logicalX * m_viewport.scale,
|
||||||
|
m_viewport.dst.y + logicalY * m_viewport.scale
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_FRect dst {
|
||||||
|
winPos.x,
|
||||||
|
winPos.y,
|
||||||
|
static_cast<float>(cached.width),
|
||||||
|
static_cast<float>(cached.height)
|
||||||
|
};
|
||||||
|
//std::cout << "Rendering text at (" << dst.x << ", " << dst.y << ") with size (" << dst.w << ", " << dst.h << ")\n";
|
||||||
|
|
||||||
// 渲染
|
// 渲染
|
||||||
SDL_FRect dest = { static_cast<float>(x), static_cast<float>(y),
|
SDL_RenderTexture(
|
||||||
static_cast<float>(cached.width),
|
m_renderer,
|
||||||
static_cast<float>(cached.height) };
|
cached.texture,
|
||||||
SDL_RenderTexture(m_renderer, cached.texture, NULL, &dest);
|
nullptr,
|
||||||
|
&dst
|
||||||
|
);
|
||||||
|
|
||||||
//m_bitmapFont->drawText(text, x, y);
|
//m_bitmapFont->drawText(text, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string& text, TextStyle style) {
|
TextRenderer::CachedText& TextRenderer::createAndCacheTexture(const std::string& text, TextStyle style) {
|
||||||
CachedText result = {nullptr, 0, 0, std::time(nullptr)};
|
auto key = makeHash(text, style);
|
||||||
|
CachedText& slot = m_cache[key];
|
||||||
|
// empty: 用于失败返回的只读哨兵对象(texture == nullptr)
|
||||||
|
static CachedText empty{};
|
||||||
|
// 如果已经存在,直接返回
|
||||||
|
if (slot.texture) {
|
||||||
|
slot.lastAccessTime = std::time(nullptr);
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
if (slot.texture) {
|
||||||
|
SDL_DestroyTexture(slot.texture);
|
||||||
|
}
|
||||||
|
// 创建逻辑
|
||||||
|
slot = {}; // reset
|
||||||
|
slot.lastAccessTime = std::time(nullptr);
|
||||||
/*
|
/*
|
||||||
// 获取字体 - 需要大号字体用于高清渲染
|
// 获取字体 - 需要大号字体用于高清渲染
|
||||||
const int TARGET_SCALE = 4; // 4倍超采样
|
const int TARGET_SCALE = 4; // 4倍超采样
|
||||||
@@ -129,17 +145,19 @@ TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string&
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// 获取字体
|
// 获取字体
|
||||||
TTF_Font* font = m_fontManager->getFont(style.fontID, style.fontSize);
|
TTF_Font* font = m_fontManager->getFont(style.fontID, style.fontSize * m_viewport.scale);
|
||||||
if (!font) {
|
if (!font) {
|
||||||
SDL_Log("错误:字体未找到 %s\n", style.fontID.c_str());
|
SDL_Log("错误:字体未找到 %s\n", style.fontID.c_str());
|
||||||
return result;
|
m_cache.erase(key);
|
||||||
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建文字表面
|
// 创建文字表面
|
||||||
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str(),text.length(), style.color);
|
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str(),text.length(), style.color);
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
SDL_Log("错误:无法创建文字表面 '%s'\n", text.c_str());
|
SDL_Log("错误:无法创建文字表面 '%s'\n", text.c_str());
|
||||||
return result;
|
m_cache.erase(key);
|
||||||
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建纹理
|
// 创建纹理
|
||||||
@@ -150,26 +168,24 @@ TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string&
|
|||||||
|
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
SDL_Log("错误:无法创建纹理\n");
|
SDL_Log("错误:无法创建纹理\n");
|
||||||
return result;
|
m_cache.erase(key);
|
||||||
|
return empty;
|
||||||
}
|
}
|
||||||
// 设置纹理缩放模式为最近邻
|
// 设置纹理缩放模式为最近邻
|
||||||
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
//SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
||||||
// 保存结果
|
// 保存结果
|
||||||
result.texture = texture;
|
slot.texture = texture;
|
||||||
result.width = width;
|
slot.width = width;
|
||||||
result.height = height;
|
slot.height = height;
|
||||||
result.lastAccessTime = std::time(nullptr);
|
slot.lastAccessTime = std::time(nullptr);
|
||||||
|
|
||||||
// 保存到缓存
|
|
||||||
auto key = makeHash(text, style);
|
|
||||||
m_cache[key] = result;
|
|
||||||
|
|
||||||
|
|
||||||
// 检查是否需要清理缓存
|
// 检查是否需要清理缓存
|
||||||
if (m_cache.size() > MAX_CACHE_SIZE) {
|
if (m_cache.size() > MAX_CACHE_SIZE) {
|
||||||
autoCleanCache();
|
autoCleanCache(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -208,7 +224,7 @@ void TextRenderer::clearCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TextRenderer::autoCleanCache() {
|
void TextRenderer::autoCleanCache(size_t keepKey) {
|
||||||
// 如果缓存没有超过最大限制,则不进行清理
|
// 如果缓存没有超过最大限制,则不进行清理
|
||||||
if (m_cache.size() <= MAX_CACHE_SIZE) {
|
if (m_cache.size() <= MAX_CACHE_SIZE) {
|
||||||
return;
|
return;
|
||||||
@@ -219,6 +235,7 @@ void TextRenderer::autoCleanCache() {
|
|||||||
cacheItems.reserve(m_cache.size());
|
cacheItems.reserve(m_cache.size());
|
||||||
|
|
||||||
for (const auto& pair : m_cache) {
|
for (const auto& pair : m_cache) {
|
||||||
|
if (pair.first == keepKey) continue;
|
||||||
cacheItems.emplace_back(pair.first, pair.second.lastAccessTime);
|
cacheItems.emplace_back(pair.first, pair.second.lastAccessTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,8 +253,10 @@ void TextRenderer::autoCleanCache() {
|
|||||||
|
|
||||||
// 删除最旧的缓存项
|
// 删除最旧的缓存项
|
||||||
for (size_t i = 0; i < itemsToRemove && i < cacheItems.size(); ++i) {
|
for (size_t i = 0; i < itemsToRemove && i < cacheItems.size(); ++i) {
|
||||||
|
|
||||||
auto it = m_cache.find(cacheItems[i].first);
|
auto it = m_cache.find(cacheItems[i].first);
|
||||||
if (it != m_cache.end()) {
|
if (it != m_cache.end()) {
|
||||||
|
|
||||||
SDL_DestroyTexture(it->second.texture);
|
SDL_DestroyTexture(it->second.texture);
|
||||||
m_cache.erase(it);
|
m_cache.erase(it);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "FontManager.h"
|
#include "FontManager.h"
|
||||||
#include "BitmapFont.h"
|
#include "BitmapFont.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "utils/Config.h"
|
||||||
/**
|
/**
|
||||||
* @class TextRenderer
|
* @class TextRenderer
|
||||||
* @brief 文本渲染器
|
* @brief 文本渲染器
|
||||||
@@ -37,7 +38,7 @@ public:
|
|||||||
* @param fontManager 字体管理器指针,用于获取指定字体 ID 和大小的字体对象。不可为 nullptr
|
* @param fontManager 字体管理器指针,用于获取指定字体 ID 和大小的字体对象。不可为 nullptr
|
||||||
* @details 初始化文本渲染器,设置必要的 SDL 渲染器和字体管理器引用
|
* @details 初始化文本渲染器,设置必要的 SDL 渲染器和字体管理器引用
|
||||||
*/
|
*/
|
||||||
TextRenderer(SDL_Renderer* renderer, FontManager* fontManager);
|
TextRenderer(SDL_Renderer* renderer, FontManager* fontManager, const Viewport& viewport);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 析构函数
|
* @brief 析构函数
|
||||||
@@ -56,7 +57,7 @@ public:
|
|||||||
* 这样可以避免重复渲染相同的文本。
|
* 这样可以避免重复渲染相同的文本。
|
||||||
* @note 返回的尺寸是矩形的宽和高
|
* @note 返回的尺寸是矩形的宽和高
|
||||||
*/
|
*/
|
||||||
std::pair<int, int> getTextSize(const std::string& text, TextStyle style);
|
std::pair<int, int> getLogicalTextSize(const std::string& text, TextStyle style);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将文本渲染到指定位置
|
* @brief 将文本渲染到指定位置
|
||||||
@@ -89,6 +90,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void clearCache();
|
void clearCache();
|
||||||
|
|
||||||
|
Viewport getViewport() const {return m_viewport;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** @brief SDL 渲染器指针,用于创建纹理和执行 2D 渲染操作 */
|
/** @brief SDL 渲染器指针,用于创建纹理和执行 2D 渲染操作 */
|
||||||
SDL_Renderer* m_renderer;
|
SDL_Renderer* m_renderer;
|
||||||
@@ -96,6 +99,9 @@ private:
|
|||||||
/** @brief 字体管理器指针,用于管理和获取字体资源 */
|
/** @brief 字体管理器指针,用于管理和获取字体资源 */
|
||||||
FontManager* m_fontManager;
|
FontManager* m_fontManager;
|
||||||
|
|
||||||
|
/** @brief 视口信息引用,用于坐标转换和渲染位置计算 */
|
||||||
|
const Viewport& m_viewport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct CachedText
|
* @struct CachedText
|
||||||
* @brief 缓存的文本纹理信息
|
* @brief 缓存的文本纹理信息
|
||||||
@@ -136,7 +142,7 @@ private:
|
|||||||
static constexpr size_t MIN_CACHE_SIZE = 128;
|
static constexpr size_t MIN_CACHE_SIZE = 128;
|
||||||
|
|
||||||
/** @brief 自动清理缓存(当缓存超过最大限制时调用) */
|
/** @brief 自动清理缓存(当缓存超过最大限制时调用) */
|
||||||
void autoCleanCache();
|
void autoCleanCache(size_t keepKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建文本纹理(暂未使用)
|
* @brief 创建文本纹理(暂未使用)
|
||||||
@@ -163,6 +169,6 @@ private:
|
|||||||
* 6. 返回结果
|
* 6. 返回结果
|
||||||
* @see CachedText, m_cache
|
* @see CachedText, m_cache
|
||||||
*/
|
*/
|
||||||
CachedText createAndCacheTexture(const std::string& text, TextStyle style);
|
CachedText& createAndCacheTexture(const std::string& text, TextStyle style);
|
||||||
//std::unique_ptr<BitmapFont> m_bitmapFont;
|
//std::unique_ptr<BitmapFont> m_bitmapFont;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,12 +69,17 @@ void UIRenderer::renderButtonBackground(const ButtonData& buttonData) {
|
|||||||
auto m_rect = buttonData.rect;
|
auto m_rect = buttonData.rect;
|
||||||
|
|
||||||
SDL_SetRenderDrawColor(m_renderer, m_backgroundColor.r, m_backgroundColor.g, m_backgroundColor.b, m_backgroundColor.a);
|
SDL_SetRenderDrawColor(m_renderer, m_backgroundColor.r, m_backgroundColor.g, m_backgroundColor.b, m_backgroundColor.a);
|
||||||
auto [width, height] = m_textRenderer->getTextSize(buttonData.text, buttonData.textstytle);
|
//auto [width, height] = m_textRenderer->getLogicalTextSize(buttonData.text, buttonData.textstytle);
|
||||||
|
auto viewport = m_textRenderer->getViewport();
|
||||||
|
SDL_FPoint winPos = {
|
||||||
|
viewport.dst.x + m_rect.x * viewport.scale,
|
||||||
|
viewport.dst.y + m_rect.y * viewport.scale
|
||||||
|
};
|
||||||
// 绘制普通矩形
|
// 绘制普通矩形
|
||||||
SDL_FRect rect = { static_cast<float>(m_rect.x) + 0.375f,
|
SDL_FRect rect = { winPos.x ,
|
||||||
static_cast<float>(m_rect.y) + 0.375f,
|
winPos.y ,
|
||||||
static_cast<float>(m_rect.w) - 0.75f,
|
static_cast<float>(m_rect.w * viewport.scale) ,
|
||||||
static_cast<float>(m_rect.h) - 0.75f };
|
static_cast<float>(m_rect.h * viewport.scale) };
|
||||||
//SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE);
|
//SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE);
|
||||||
SDL_RenderFillRect(m_renderer, &rect);
|
SDL_RenderFillRect(m_renderer, &rect);
|
||||||
}
|
}
|
||||||
@@ -89,7 +94,7 @@ void UIRenderer::renderText(const Type& data) {
|
|||||||
auto m_rect = data.rect;
|
auto m_rect = data.rect;
|
||||||
auto m_textStyle = data.textstytle;
|
auto m_textStyle = data.textstytle;
|
||||||
if (m_text.empty()) return;
|
if (m_text.empty()) return;
|
||||||
|
// 这个计算公式有问题,导致放大之后文字位置会出现问题,故舍弃
|
||||||
// 计算文本位置(居中)
|
// 计算文本位置(居中)
|
||||||
// 这里需要TextRenderer的实际实现
|
// 这里需要TextRenderer的实际实现
|
||||||
// 假设TextRenderer有一个renderText方法:
|
// 假设TextRenderer有一个renderText方法:
|
||||||
@@ -97,7 +102,7 @@ void UIRenderer::renderText(const Type& data) {
|
|||||||
// const TextStyle& style, int x, int y);
|
// const TextStyle& style, int x, int y);
|
||||||
|
|
||||||
// 获取文本实际尺寸以便正确居中渲染
|
// 获取文本实际尺寸以便正确居中渲染
|
||||||
auto [textW, textH] = m_textRenderer->getTextSize(m_text, m_textStyle);
|
/*auto [textW, textH] = m_textRenderer->getLogicalTextSize(m_text, m_textStyle);
|
||||||
|
|
||||||
// 如果组件的宽高为 0,则使用文本尺寸填充(相当于自动调整控件大小)
|
// 如果组件的宽高为 0,则使用文本尺寸填充(相当于自动调整控件大小)
|
||||||
int boxW = m_rect.w;
|
int boxW = m_rect.w;
|
||||||
@@ -108,7 +113,7 @@ void UIRenderer::renderText(const Type& data) {
|
|||||||
// 计算文本左上角坐标以实现居中
|
// 计算文本左上角坐标以实现居中
|
||||||
int textX = m_rect.x + (boxW - textW) / 2;
|
int textX = m_rect.x + (boxW - textW) / 2;
|
||||||
int textY = m_rect.y + (boxH - textH) / 2;
|
int textY = m_rect.y + (boxH - textH) / 2;
|
||||||
|
*/
|
||||||
// 渲染文本(TextRenderer 的 x,y 为左上角)
|
// 渲染文本(TextRenderer 的 x,y 为左上角)
|
||||||
m_textRenderer->renderText(m_text, m_textStyle, textX, textY);
|
m_textRenderer->renderText(m_text, m_textStyle, m_rect.x, m_rect.y);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user