Extra2D/include/resource/font_atlas.h

179 lines
4.6 KiB
C++

#pragma once
#include <resource/resource.h>
#include <resource/texture.h>
#include <types/math/vec2.h>
#include <types/base/types.h>
#include <types/ptr/intrusive_ptr.h>
#include <unordered_map>
#include <vector>
#include <memory>
// STB 矩形打包
#include <stb/stb_rect_pack.h>
namespace extra2d {
/**
* @brief 字符信息结构
*/
struct GlyphInfo {
Vec2 uv0; // 左下角 UV 坐标
Vec2 uv1; // 右上角 UV 坐标
Vec2 size; // 像素尺寸
Vec2 bearing; // 基线偏移(左下角相对原点的偏移)
float advance; // 水平步进(到下一个字符的距离)
uint32 pageIndex; // 所在图集页索引
bool valid = false; // 是否有效
};
/**
* @brief 图集页结构
*
* 使用 stb_rect_pack 进行矩形打包
*/
struct AtlasPage {
static constexpr uint32 DEFAULT_SIZE = 1024; // 默认图集尺寸
Ptr<Texture> texture; // 纹理
stbrp_context packContext; // 打包上下文
std::vector<stbrp_node> packNodes; // 打包节点数组
std::vector<uint8> pixelData; // CPU 端像素数据
uint32 width = DEFAULT_SIZE; // 图集宽度
uint32 height = DEFAULT_SIZE; // 图集高度
bool dirty = false; // 是否需要更新 GPU 纹理
/**
* @brief 构造函数
*/
AtlasPage();
/**
* @brief 初始化图集页
* @param size 图集尺寸
* @return 是否初始化成功
*/
bool init(uint32 size);
/**
* @brief 打包矩形到图集
* @param width 矩形宽度
* @param height 矩形高度
* @param outX 输出 X 坐标
* @param outY 输出 Y 坐标
* @return 是否打包成功
*/
bool packRect(uint32 width, uint32 height, uint32& outX, uint32& outY);
/**
* @brief 写入像素数据到图集
* @param x 起始 X 坐标
* @param y 起始 Y 坐标
* @param width 宽度
* @param height 高度
* @param data 像素数据(单通道)
*/
void writePixels(uint32 x, uint32 y, uint32 width, uint32 height, const uint8* data);
/**
* @brief 更新 GPU 纹理
*/
void updateTexture();
/**
* @brief 清空图集
*/
void clear();
};
/**
* @brief 字体图集类
*
* 管理动态字符图集,支持多页扩展
*/
class FontAtlas : public Resource {
public:
FontAtlas();
~FontAtlas() override;
/**
* @brief 获取资源类型
*/
ResourceType getType() const override { return ResourceType::FontAtlas; }
/**
* @brief 初始化字体图集
* @param pageSize 图集页尺寸
* @return 是否初始化成功
*/
bool init(uint32 pageSize = AtlasPage::DEFAULT_SIZE);
/**
* @brief 添加字符到图集
* @param codepoint Unicode 码点
* @param bitmap 字符位图数据(单通道)
* @param width 位图宽度
* @param height 位图高度
* @param bearingX 水平基线偏移
* @param bearingY 垂直基线偏移
* @param advance 水平步进
* @return 是否添加成功
*/
bool addGlyph(uint32 codepoint, const uint8* bitmap, uint32 width, uint32 height,
float bearingX, float bearingY, float advance);
/**
* @brief 获取字符信息
* @param codepoint Unicode 码点
* @return 字符信息指针,如果不存在返回 nullptr
*/
const GlyphInfo* getGlyph(uint32 codepoint) const;
/**
* @brief 检查字符是否已缓存
* @param codepoint Unicode 码点
* @return 是否已缓存
*/
bool hasGlyph(uint32 codepoint) const;
/**
* @brief 获取图集页数量
*/
uint32 getPageCount() const { return static_cast<uint32>(pages_.size()); }
/**
* @brief 获取指定页的纹理
* @param pageIndex 页索引
* @return 纹理指针
*/
Texture* getPageTexture(uint32 pageIndex) const;
/**
* @brief 更新所有需要更新的图集页纹理
*/
void updateTextures();
/**
* @brief 清空所有图集
*/
void clear();
/**
* @brief 获取图集页尺寸
*/
uint32 getPageSize() const { return pageSize_; }
private:
std::vector<std::unique_ptr<AtlasPage>> pages_; // 图集页数组
std::unordered_map<uint32, GlyphInfo> glyphs_; // 字符信息映射表
uint32 pageSize_ = AtlasPage::DEFAULT_SIZE; // 图集页尺寸
/**
* @brief 获取或创建可用的图集页
* @return 图集页指针
*/
AtlasPage* getOrCreatePage();
};
} // namespace extra2d