Files
lity3d/Engine/Editor/glyph_atlas.c
T
shuiyu1123 2d56cb3bc9 MiMoEngine ASM: x86-64 MASM汇编+C混合架构游戏引擎
核心热路径(数学/内存/字符串/性能分析)用MASM汇编(SSE/AVX2)
外围系统(窗口/渲染/ECS/音频/UI/脚本)用C语言
含自研编辑器、VisualScript积木编程、中文渲染(glyph_atlas)
2026-07-10 19:20:40 +08:00

172 lines
5.1 KiB
C

#include "glyph_atlas.h"
#include <stdlib.h>
#include <string.h>
#include <d3d11.h>
#include <Windows.h>
#include "../Core/logger.h"
typedef struct {
wchar_t ch;
int x, y;
int w, h;
float u0, v0, u1, v1;
float advance_x;
} GlyphEntry;
#define MAX_GLYPHS 1024
static GlyphEntry g_entries[MAX_GLYPHS];
static int g_entry_count = 0;
int glyph_atlas_init(GlyphAtlas* ga, ID3D11Device* dev) {
memset(ga, 0, sizeof(*ga));
ga->cell_w = GLYPH_CELL_W;
ga->cell_h = GLYPH_CELL_H;
ga->atlas_w = GLYPH_ATLAS_SIZE;
ga->atlas_h = GLYPH_ATLAS_SIZE;
ga->next_x = 0;
ga->next_y = 0;
ga->u_scale = 1.0f / (float)ga->atlas_w;
ga->v_scale = 1.0f / (float)ga->atlas_h;
uint8_t* pixels = (uint8_t*)calloc(ga->atlas_w * ga->atlas_h * 4, 1);
D3D11_TEXTURE2D_DESC td = {0};
td.Width = ga->atlas_w;
td.Height = ga->atlas_h;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA sd = {0};
sd.pSysMem = pixels;
sd.SysMemPitch = ga->atlas_w * 4;
HRESULT hr = dev->lpVtbl->CreateTexture2D(dev, &td, &sd, &ga->texture);
free(pixels);
if (FAILED(hr)) return 0;
dev->lpVtbl->CreateShaderResourceView(dev, (ID3D11Resource*)ga->texture, NULL, &ga->srv);
LOG_INFO("GlyphAtlas: initialized (%dx%d, max %d glyphs)", ga->atlas_w, ga->atlas_h, MAX_GLYPHS);
return 1;
}
void glyph_atlas_destroy(GlyphAtlas* ga) {
if (!ga) return;
if (ga->srv) ga->srv->lpVtbl->Release(ga->srv);
if (ga->texture) ga->texture->lpVtbl->Release(ga->texture);
memset(ga, 0, sizeof(*ga));
g_entry_count = 0;
}
static int find_glyph(wchar_t ch) {
for (int i = 0; i < g_entry_count; i++) {
if (g_entries[i].ch == ch) return i;
}
return -1;
}
int glyph_atlas_get_or_create(GlyphAtlas* ga, ID3D11Device* dev, ID3D11DeviceContext* ctx, wchar_t ch,
float* u0, float* v0, float* u1, float* v1,
float* advance_x, float* advance_y) {
int idx = find_glyph(ch);
if (idx >= 0) {
*u0 = g_entries[idx].u0;
*v0 = g_entries[idx].v0;
*u1 = g_entries[idx].u1;
*v1 = g_entries[idx].v1;
*advance_x = g_entries[idx].advance_x;
*advance_y = (float)g_entries[idx].h;
return 1;
}
if (g_entry_count >= MAX_GLYPHS) return 0;
if (ga->next_x + ga->cell_w > ga->atlas_w) {
ga->next_x = 0;
ga->next_y += ga->cell_h;
}
if (ga->next_y + ga->cell_h > ga->atlas_h) return 0;
HDC hdc = GetDC(NULL);
HFONT hfont = CreateFontW(
ga->cell_h - 4, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, FIXED_PITCH | FF_DONTCARE, L"Microsoft YaHei"
);
HFONT old = (HFONT)SelectObject(hdc, hfont);
GLYPHMETRICS gm = {0};
MAT2 mat = {{0,0},{0,0},{0,0},{0,0}};
mat.eM11.value = 1; mat.eM22.value = 1;
DWORD size = GetGlyphOutlineW(hdc, ch, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat);
int gw = gm.gmBlackBoxX;
int gh = gm.gmBlackBoxY;
int gx = ga->next_x;
int gy = ga->next_y;
if (size > 0 && gw > 0 && gh > 0) {
uint8_t* buf = (uint8_t*)malloc(size);
GetGlyphOutlineW(hdc, ch, GGO_GRAY8_BITMAP, &gm, size, buf, &mat);
uint8_t* pixels = (uint8_t*)calloc(gw * gh * 4, 1);
int row_bytes = ((gw + 3) / 4) * 4;
for (int row = 0; row < gh; row++) {
for (int col = 0; col < gw; col++) {
int src_idx = row * row_bytes + col;
if (src_idx < (int)size) {
uint8_t gray = buf[src_idx];
uint8_t alpha = (gray > 200) ? 255 : (gray > 100) ? 200 : (gray > 30) ? 128 : 0;
int idx = (row * gw + col) * 4;
pixels[idx] = 255;
pixels[idx + 1] = 255;
pixels[idx + 2] = 255;
pixels[idx + 3] = alpha;
}
}
}
D3D11_BOX box = {0};
box.left = gx;
box.top = gy;
box.front = 0;
box.right = gx + gw;
box.bottom = gy + gh;
box.back = 1;
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)ga->texture, 0, &box, pixels, gw * 4, 0);
free(buf);
free(pixels);
}
SelectObject(hdc, old);
DeleteObject(hfont);
ReleaseDC(NULL, hdc);
GlyphEntry* e = &g_entries[g_entry_count++];
e->ch = ch;
e->x = gx;
e->y = gy;
e->w = gw > 0 ? gw : ga->cell_w / 2;
e->h = gh > 0 ? gh : ga->cell_h;
e->u0 = (float)gx * ga->u_scale;
e->v0 = (float)gy * ga->v_scale;
e->u1 = (float)(gx + e->w) * ga->u_scale;
e->v1 = (float)(gy + e->h) * ga->v_scale;
e->advance_x = (float)(gm.gmCellIncX > 0 ? gm.gmCellIncX : ga->cell_w / 2);
ga->next_x += ga->cell_w;
*u0 = e->u0;
*v0 = e->v0;
*u1 = e->u1;
*v1 = e->v1;
*advance_x = e->advance_x;
*advance_y = (float)e->h;
return 1;
}