#include "glyph_atlas.h" #include #include #include #include #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; }