核心热路径(数学/内存/字符串/性能分析)用MASM汇编(SSE/AVX2) 外围系统(窗口/渲染/ECS/音频/UI/脚本)用C语言 含自研编辑器、VisualScript积木编程、中文渲染(glyph_atlas)
172 lines
5.1 KiB
C
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;
|
|
}
|