mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 14:24:10 +08:00
Added a lots of the file to write the pixel game but due to some problem ,i dicide to change sdl3 to sfml,a more modern lib
This commit is contained in:
208
src/graphics/font/BitmapFont.cpp
Normal file
208
src/graphics/font/BitmapFont.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include "BitmapFont.h"
|
||||
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
static bool startsWith(const std::string& s, const char* prefix) {
|
||||
return s.size() >= std::strlen(prefix) &&
|
||||
s.compare(0, std::strlen(prefix), prefix) == 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// 构造 / 析构
|
||||
////////////////////////////////////////////////////////////
|
||||
BitmapFont::BitmapFont() = default;
|
||||
|
||||
BitmapFont::~BitmapFont() {
|
||||
for (SDL_Texture* tex : m_pages) {
|
||||
SDL_DestroyTexture(tex);
|
||||
}
|
||||
m_pages.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// 加载 .fnt
|
||||
////////////////////////////////////////////////////////////
|
||||
bool BitmapFont::load(const std::string& fntPath, SDL_Renderer* renderer) {
|
||||
m_renderer = renderer;
|
||||
|
||||
std::ifstream file(fntPath);
|
||||
if (!file.is_open()) {
|
||||
SDL_Log("BitmapFont: 无法打开 %s", fntPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// .fnt 所在目录(用于加载 .tga)
|
||||
std::string baseDir =
|
||||
std::filesystem::path(fntPath).parent_path().string();
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
|
||||
// common 行:行高
|
||||
if (startsWith(line, "common ")) {
|
||||
sscanf(line.c_str(),
|
||||
"common lineHeight=%d",
|
||||
&m_lineHeight
|
||||
);
|
||||
}
|
||||
|
||||
// page 行:加载贴图
|
||||
else if (startsWith(line, "page ")) {
|
||||
int id = 0;
|
||||
char filename[256]{};
|
||||
|
||||
sscanf(line.c_str(),
|
||||
"page id=%d file=\"%255[^\"]\"",
|
||||
&id, filename
|
||||
);
|
||||
|
||||
if ((int)m_pages.size() <= id) {
|
||||
m_pages.resize(id + 1, nullptr);
|
||||
}
|
||||
|
||||
std::string texPath = baseDir + "/" + filename;
|
||||
|
||||
SDL_Surface* surf = IMG_Load(texPath.c_str());
|
||||
if (!surf) {
|
||||
SDL_Log("BitmapFont: 无法加载贴图 %s", texPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Texture* tex =
|
||||
SDL_CreateTextureFromSurface(renderer, surf);
|
||||
SDL_DestroySurface(surf);
|
||||
|
||||
// 像素风关键:最近邻
|
||||
SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
m_pages[id] = tex;
|
||||
}
|
||||
|
||||
// char 行:字形数据
|
||||
else if (startsWith(line, "char ")) {
|
||||
uint32_t id = 0;
|
||||
BitmapGlyph g{};
|
||||
|
||||
sscanf(line.c_str(),
|
||||
"char id=%u x=%d y=%d width=%d height=%d "
|
||||
"xoffset=%d yoffset=%d xadvance=%d page=%d",
|
||||
&id,
|
||||
&g.x, &g.y,
|
||||
&g.w, &g.h,
|
||||
&g.xOffset, &g.yOffset,
|
||||
&g.xAdvance,
|
||||
&g.page
|
||||
);
|
||||
|
||||
m_glyphs[id] = g;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// 绘制文本
|
||||
////////////////////////////////////////////////////////////
|
||||
void BitmapFont::drawText(const std::string& text, int x, int y) const {
|
||||
int cursorX = x;
|
||||
int cursorY = y;
|
||||
|
||||
for (uint32_t cp : utf8ToCodepoints(text)) {
|
||||
|
||||
// 换行
|
||||
if (cp == '\n') {
|
||||
cursorX = x;
|
||||
cursorY += m_lineHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = m_glyphs.find(cp);
|
||||
if (it == m_glyphs.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const BitmapGlyph& g = it->second;
|
||||
|
||||
SDL_FRect src{
|
||||
(float)g.x,
|
||||
(float)g.y,
|
||||
(float)g.w,
|
||||
(float)g.h
|
||||
};
|
||||
|
||||
SDL_FRect dst{
|
||||
(float)(cursorX + g.xOffset),
|
||||
(float)(cursorY + g.yOffset),
|
||||
(float)g.w,
|
||||
(float)g.h
|
||||
};
|
||||
|
||||
SDL_RenderTexture(
|
||||
m_renderer,
|
||||
m_pages[g.page],
|
||||
&src,
|
||||
&dst
|
||||
);
|
||||
|
||||
cursorX += g.xAdvance;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// 行高
|
||||
////////////////////////////////////////////////////////////
|
||||
int BitmapFont::getLineHeight() const {
|
||||
return m_lineHeight;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// UTF-8 → Unicode codepoint(最小但够用)
|
||||
////////////////////////////////////////////////////////////
|
||||
std::vector<uint32_t>
|
||||
BitmapFont::utf8ToCodepoints(const std::string& text) {
|
||||
std::vector<uint32_t> result;
|
||||
|
||||
for (size_t i = 0; i < text.size();) {
|
||||
uint8_t c = (uint8_t)text[i];
|
||||
|
||||
if (c < 0x80) {
|
||||
result.push_back(c);
|
||||
i += 1;
|
||||
}
|
||||
else if ((c >> 5) == 0x6) {
|
||||
uint32_t cp =
|
||||
((text[i] & 0x1F) << 6) |
|
||||
(text[i + 1] & 0x3F);
|
||||
result.push_back(cp);
|
||||
i += 2;
|
||||
}
|
||||
else if ((c >> 4) == 0xE) {
|
||||
uint32_t cp =
|
||||
((text[i] & 0x0F) << 12) |
|
||||
((text[i + 1] & 0x3F) << 6) |
|
||||
(text[i + 2] & 0x3F);
|
||||
result.push_back(cp);
|
||||
i += 3;
|
||||
}
|
||||
else if ((c >> 3) == 0x1E) {
|
||||
uint32_t cp =
|
||||
((text[i] & 0x07) << 18) |
|
||||
((text[i + 1] & 0x3F) << 12) |
|
||||
((text[i + 2] & 0x3F) << 6) |
|
||||
(text[i + 3] & 0x3F);
|
||||
result.push_back(cp);
|
||||
i += 4;
|
||||
}
|
||||
else {
|
||||
// 非法 UTF-8,跳过
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
70
src/graphics/font/BitmapFont.h
Normal file
70
src/graphics/font/BitmapFont.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
BitmapFont.h
|
||||
--------------------------------
|
||||
Bitmap Font(BMFont / AngelCode)渲染器
|
||||
- 支持 .fnt(文本格式)
|
||||
- 支持多 page(多张 .tga)
|
||||
- 支持 UTF-8 / 中文
|
||||
- SDL3 / 像素风(NEAREST)
|
||||
|
||||
用法示例:
|
||||
BitmapFont font;
|
||||
font.load("assets/fonts/sanhan.fnt", renderer);
|
||||
font.drawText("你好,像素世界", 10, 10);
|
||||
*/
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// 单个字形(对应 .fnt 中的 char 行)
|
||||
////////////////////////////////////////////////////////////
|
||||
struct BitmapGlyph {
|
||||
int x = 0; // 在贴图中的 x
|
||||
int y = 0; // 在贴图中的 y
|
||||
int w = 0; // 字形宽度
|
||||
int h = 0; // 字形高度
|
||||
|
||||
int xOffset = 0; // 绘制时 x 偏移
|
||||
int yOffset = 0; // 绘制时 y 偏移
|
||||
int xAdvance = 0;// 光标前进量
|
||||
|
||||
int page = 0; // 所属贴图页
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// BitmapFont 类
|
||||
////////////////////////////////////////////////////////////
|
||||
class BitmapFont {
|
||||
public:
|
||||
BitmapFont();
|
||||
~BitmapFont();
|
||||
|
||||
// 加载 .fnt 文件
|
||||
bool load(const std::string& fntPath, SDL_Renderer* renderer);
|
||||
|
||||
// 绘制文本(UTF-8)
|
||||
void drawText(const std::string& text, int x, int y) const;
|
||||
|
||||
// 获取行高(用于换行 / UI 布局)
|
||||
int getLineHeight() const;
|
||||
|
||||
private:
|
||||
// UTF-8 → Unicode codepoint
|
||||
static std::vector<uint32_t> utf8ToCodepoints(const std::string& text);
|
||||
|
||||
private:
|
||||
SDL_Renderer* m_renderer = nullptr;
|
||||
|
||||
int m_lineHeight = 0;
|
||||
|
||||
// Unicode → Glyph
|
||||
std::unordered_map<uint32_t, BitmapGlyph> m_glyphs;
|
||||
|
||||
// 所有 page 对应的纹理
|
||||
std::vector<SDL_Texture*> m_pages;
|
||||
};
|
||||
@@ -5,7 +5,8 @@ TextRenderer::TextRenderer(SDL_Renderer* renderer, FontManager* fontManager) :
|
||||
m_fontManager(fontManager),
|
||||
m_renderer(renderer)
|
||||
{
|
||||
|
||||
//m_bitmapFont = std::make_unique<BitmapFont>();
|
||||
//m_bitmapFont->load("assets/fonts/sanhan.fnt", renderer);
|
||||
}
|
||||
|
||||
TextRenderer::~TextRenderer() {
|
||||
@@ -62,6 +63,8 @@ void TextRenderer::renderText(const std::string& text, TextStyle style, int x, i
|
||||
static_cast<float>(cached.width),
|
||||
static_cast<float>(cached.height) };
|
||||
SDL_RenderTexture(m_renderer, cached.texture, NULL, &dest);
|
||||
|
||||
//m_bitmapFont->drawText(text, x, y);
|
||||
}
|
||||
|
||||
TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string& text, TextStyle style) {
|
||||
@@ -91,6 +94,7 @@ TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string&
|
||||
SDL_Log("错误:无法创建纹理\n");
|
||||
return result;
|
||||
}
|
||||
// 设置纹理缩放模式为最近邻
|
||||
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
||||
// 保存结果
|
||||
result.texture = texture;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#include "Textstyle.h"
|
||||
#include "FontManager.h"
|
||||
|
||||
#include "BitmapFont.h"
|
||||
#include <memory>
|
||||
/**
|
||||
* @class TextRenderer
|
||||
* @brief 文本渲染器
|
||||
@@ -163,5 +164,5 @@ private:
|
||||
* @see CachedText, m_cache
|
||||
*/
|
||||
CachedText createAndCacheTexture(const std::string& text, TextStyle style);
|
||||
|
||||
//std::unique_ptr<BitmapFont> m_bitmapFont;
|
||||
};
|
||||
|
||||
@@ -99,14 +99,14 @@ void UIRenderer::renderText(const Type& data) {
|
||||
auto [textW, textH] = m_textRenderer->getTextSize(m_text, m_textStyle);
|
||||
|
||||
// 如果组件的宽高为 0,则使用文本尺寸填充(相当于自动调整控件大小)
|
||||
float boxW = m_rect.w;
|
||||
float boxH = m_rect.h;
|
||||
if (boxW <= 0.0f) boxW = static_cast<float>(textW);
|
||||
if (boxH <= 0.0f) boxH = static_cast<float>(textH);
|
||||
int boxW = m_rect.w;
|
||||
int boxH = m_rect.h;
|
||||
if (boxW <= 0) boxW = textW;
|
||||
if (boxH <= 0) boxH = textH;
|
||||
|
||||
// 计算文本左上角坐标以实现居中
|
||||
int textX = static_cast<int>(m_rect.x + (boxW - textW) / 2.0f);
|
||||
int textY = static_cast<int>(m_rect.y + (boxH - textH) / 2.0f);
|
||||
int textX = m_rect.x + (boxW - textW) / 2;
|
||||
int textY = m_rect.y + (boxH - textH) / 2;
|
||||
|
||||
// 渲染文本(TextRenderer 的 x,y 为左上角)
|
||||
m_textRenderer->renderText(m_text, m_textStyle, textX, textY);
|
||||
|
||||
Reference in New Issue
Block a user