From 2780f811f0647eb1fddbe962f1cecb12ef89f06e Mon Sep 17 00:00:00 2001 From: matthew Date: Wed, 13 Aug 2025 07:45:00 +1000 Subject: [PATCH] start work on font atlas creation --- dub.json | 4 +- src/gears/game.d | 13 +++-- src/gears/main.d | 10 ++++ src/shared/font.d | 131 ++++++++++++++++++++++++++++++++++++++++++ src/shared/includes.c | 3 + 5 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 src/shared/font.d diff --git a/dub.json b/dub.json index 7ba2a01..fae2577 100644 --- a/dub.json +++ b/dub.json @@ -11,11 +11,11 @@ "sourceFiles-windows": [], "importPaths": ["src/gears", "src/codegen", "src/shared", "external/xxhash", "external/inteli"], "sourcePaths": ["src/gears", "src/codegen", "src/shared", "external/xxhash", "external/inteli"], - "libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++", "xcb-xfixes"], + "libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++", "xcb-xfixes", "freetype"], "libs-windows": [], "preGenerateCommands-linux": ["./build.sh", "build/Packer"], "preGenerateCommands-windows": [], - "dflags": ["-Xcc=-mno-sse"], + "dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2"], "dflags-dmd": ["-P=-DSTBI_NO_SIMD"] }, { diff --git a/src/gears/game.d b/src/gears/game.d index aa579dc..9a89048 100644 --- a/src/gears/game.d +++ b/src/gears/game.d @@ -116,7 +116,8 @@ InitGame(PlatformWindow* window) u8[] img_slice = img[0 .. width * height * 4]; CreateImageView(&g.rd, &g.font_tex, FONT_ATLAS.width, FONT_ATLAS.height, 4, img_slice); - CreateImageView(&g.rd, &g.default_tex, 16, 16, 4, white_tex); + // TODO: fix buffer overflow between two textures + //CreateImageView(&g.rd, &g.default_tex, 16, 16, 4, white_tex); WriteGUI(&g.rd, g.ui_desc_set, &g.font_tex); @@ -176,7 +177,7 @@ Cycle(Game* g) Reset(&g.frame_arena); DrawRect(g, 500.0, 500.0, 800.0, 800.0, Vec4(0.2, 0.3, 0.7, 1.0)); - DrawText(g, 200.0, 200.0, 32.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + DrawText(g, 200.0, 200.0, FONT_ATLAS.size, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); BeginFrame(&g.rd); @@ -330,13 +331,15 @@ DrawText(Game* g, f32 x, f32 y, f32 px, string str) { UIVertex* v = g.ui_vertex_buf.ptr + g.ui_count; - f32 w = px * (glyph.plane_right - glyph.plane_left); + f32 r = px * glyph.plane_right; + f32 l = px * glyph.plane_left; + f32 w = r - l; f32 h = px * (glyph.plane_bottom - glyph.plane_top); f32 y_pos = px * glyph.plane_bottom; - v.dst_start.x = x_pos; + v.dst_start.x = x_pos + l; v.dst_start.y = y + h - y_pos; - v.dst_end.x = x_pos + w; + v.dst_end.x = x_pos + w + l; v.dst_end.y = y - y_pos; v.src_start.x = glyph.atlas_left; diff --git a/src/gears/main.d b/src/gears/main.d index 56f785e..d00d4f8 100644 --- a/src/gears/main.d +++ b/src/gears/main.d @@ -9,6 +9,10 @@ import core.simd; import math; import core.stdc.string : memcpy; +import font; +import alloc; +import assets; + // TODO: // 1. Determine how to better handle inputs // 2. Set up multisampling @@ -19,6 +23,12 @@ void main(string[] argv) PlatformWindow window = CreateWindow("Video Game", 1920, 1080); Game g = InitGame(&window); + Arena arena = CreateArena(MB(8)); + InitFreeType(); + u8[] font_data = LoadAssetData(&arena, "fonts/NuberNextCondensed-DemiBold.otf"); + FontFace font = OpenFont(font_data); + FontAtlasBuf atlas = CreateAtlas(&arena, font, 32.0, 256); + while (true) { HandleEvents(&window); diff --git a/src/shared/font.d b/src/shared/font.d new file mode 100644 index 0000000..237e0ab --- /dev/null +++ b/src/shared/font.d @@ -0,0 +1,131 @@ +import aliases; +import includes; +import util; +import alloc; + +FT_Library FT_LIB; +alias FontFace = FT_Face; + +struct FontAtlasBuf +{ + u8[] data; + FontAtlas atlas; +} + +void +InitFreeType() +{ + FT_Init_FreeType(&FT_LIB); +} + +FontFace +OpenFont(u8[] data) +{ + FontFace font; + FT_New_Memory_Face(FT_LIB, data.ptr, cast(FT_Long)data.length, 0, &font); + return font; +} + +void +CloseFont(FontFace font) +{ + if (font != null) + { + FT_Done_Face(font); + } +} + +FontAtlasBuf +CreateAtlas(Arena* arena, FontFace font, f32 size, u64 dimension) +{ + assert(dimension >= 128, "Dimension must be at least 128"); + + FontAtlasBuf atlas = { + data: AllocArray!(u8)(arena, dimension * dimension * 4), + }; + + // TODO: proper packing algorithm + if (font != null) + { + FT_Set_Pixel_Sizes(font, 0, cast(FT_UInt)((96.0/72.0) * size)); + + i64 f_ascent = cast(i64)(font.size.metrics.ascender >> 6); + i64 f_descent = cast(i64)(font.size.metrics.descender >> 6); + i64 f_height = cast(i64)(font.size.metrics.height >> 6); + + u32 max_w = 0; + u32 max_h = 0; + u32 current_h = 0; + u32 count = 0; + + FT_UInt index; + FT_ULong char_code = FT_Get_First_Char(font, &index); + while (index != 0) + { + FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); + + u32 bmp_w = font.glyph.bitmap.width; + u32 bmp_h = font.glyph.bitmap.rows; + if (max_w + bmp_w > dimension) + { + max_h += current_h; + max_w = 0; + } + + assert(max_h < dimension, "Unable to pack atlas within dimensions"); + + max_w += bmp_w; + current_h = bmp_h > current_h ? bmp_h : current_h; + count += 1; + + char_code = FT_Get_Next_Char(font, char_code, &index); + } + + atlas.atlas.glyphs = AllocArray!(Glyph)(arena, count); + + max_w = 0; + max_h = 0; + current_h = 0; + count = 0; + + char_code = FT_Get_First_Char(font, &index); + while (index != 0) + { + FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); + + FT_Bitmap* bmp = &font.glyph.bitmap; + i32 top = font.glyph.bitmap_top; + i32 left = font.glyph.bitmap_left; + + if (max_w + bmp.rows > dimension) + { + max_h += current_h; + max_w = 0; + } + + foreach(r; 0 .. bmp.rows) + { + i32 y = cast(i32)(max_h + r); + foreach(c; 0 .. bmp.width) + { + i32 x = max_w + left + c; + u64 offset = (y*dimension + x) * 4; + + atlas.data[offset+0] = 255; + atlas.data[offset+1] = 255; + atlas.data[offset+2] = 255; + atlas.data[offset+3] = bmp.buffer[r*bmp.pitch + c]; + } + } + + max_w += bmp.width; + current_h = bmp.rows > current_h ? bmp.rows : current_h; + + char_code = FT_Get_Next_Char(font, char_code, &index); + } + + } + + return atlas; +} + diff --git a/src/shared/includes.c b/src/shared/includes.c index 5350278..6fb86fd 100644 --- a/src/shared/includes.c +++ b/src/shared/includes.c @@ -6,6 +6,9 @@ # include # include # include +# include +# include FT_FREETYPE_H +# include FT_GLYPH_H #endif #include