From be8814cda2b1cf603faa3e6bdf9b37eaf0fb03b7 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 3 Jan 2026 17:44:29 +1100 Subject: [PATCH] proper font fixes --- fonts.d | 123 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/fonts.d b/fonts.d index c56f05c..017873b 100644 --- a/fonts.d +++ b/fonts.d @@ -4,19 +4,26 @@ import dlibincludes; import dlib.aliases; import dlib.util; import dlib.alloc; +import dlib.math; +import std.math.traits : isNaN; struct FontAtlas { - f32 size; - f32 units_per_em; - f32 ascent; - f32 descent; - f32 line_gap; - f32 max_advance; - f32 line_height; - u32 width; - u32 height; - Glyph[128] glyphs; + Glyph[128] glyphs; + FontMetrics metrics; + u32 size; + UVec2 dimensions; + + alias metrics this; +} + +struct FontMetrics +{ + f32 ascent; + f32 descent; + f32 line_gap; + f32 line_height; + f32 max_advance; } struct Glyph @@ -86,39 +93,46 @@ DeMultiply(u8 color, u8 alpha) return v > 255 ? 255 : cast(u8)v; } -FontAtlasBuf -CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_top = true) +u32 +TextSize(f32 s) { - assert(dimension >= 128, "Dimension must be at least 128"); + return cast(u32)((96.0f/72.0f) * s); +} +FontAtlasBuf +CreateAtlas(Arena* arena, FontFace font, u32 size, UVec2 atlas_dim, bool y_origin_top = true) +{ FontAtlasBuf abuf = { - data: Alloc!(u8)(arena, dimension * dimension * 4), + data: Alloc!(u8)(arena, atlas_dim.x * atlas_dim.y * 4), atlas: { - size: size, - width: dimension, - height: dimension, + size: size, + dimensions: atlas_dim, }, }; // TODO: proper packing algorithm if(font != null) { - FT_Set_Pixel_Sizes(font, 0, cast(FT_UInt)((96.0f/72.0f) * size)); + FT_Set_Pixel_Sizes(font, 0, TextSize(size)); i64 bbox_ymax = font.bbox.yMax >> 6; i64 bbox_ymin = font.bbox.yMin >> 6; - abuf.atlas.line_height = size; - abuf.atlas.units_per_em = cast(f32)font.units_per_EM; - abuf.atlas.ascent = cast(f32)font.ascender; - abuf.atlas.descent = cast(f32)font.descender; - abuf.atlas.line_gap = cast(f32)(font.height - font.ascender + font.descender); - abuf.atlas.max_advance = cast(f32)(font.max_advance_width>>6); + i64 line_h = bbox_ymax - bbox_ymin; - u32 max_w, max_h, current_h, count; + u64 baseline = font.size.metrics.ascender >> 6; + u64 line_height = (font.size.metrics.height >> 6)+1; + + Logf("%s %s", baseline, line_height); + + abuf.atlas.line_height = line_height; + abuf.atlas.max_advance = font.size.metrics.max_advance >> 6; + + u32 max_w, max_h, count; i32 font_size = Float26(size); + const u32 PADDING = 2; foreach(FT_ULong char_code; 0 .. 0x7F) { FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); @@ -129,25 +143,23 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ u32 bmp_w = font.glyph.bitmap.width; u32 bmp_h = font.glyph.bitmap.rows; - if(max_w + bmp_w > dimension) + if(max_w + bmp_w > atlas_dim.x) { - max_h += current_h; + max_h += line_height+PADDING; max_w = 0; } - assert(max_h < dimension, "Unable to pack atlas within dimensions"); + if(max_h > atlas_dim.y) + { + Logf("Unable to pack atlas within dimensions provided"); + assert(false); + } max_w += bmp_w; - current_h = bmp_h > current_h ? bmp_h : current_h; count += 1; } - const u32 PADDING = 2; - - max_w = PADDING; - max_h = PADDING; - current_h = 0; - count = 0; + max_w = max_h = count = 0; foreach(FT_ULong char_code; 0 .. 0x7F) { @@ -160,24 +172,26 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ FT_GlyphSlot glyph = font.glyph; FT_Bitmap* bmp = &font.glyph.bitmap; - if(max_w + bmp.rows > dimension) + if(max_w + bmp.rows > atlas_dim.x) { - max_h += cast(u32)(size) + PADDING; - max_w = PADDING; + max_h += line_height+PADDING; + max_w = PADDING; } + i64 base = Max(baseline, font.glyph.bitmap_top); + switch(bmp.pixel_mode) { case FT_PIXEL_MODE_BGRA: { - i32 x, y; + u64 x, y; foreach(r; 0 .. bmp.rows) { - y = cast(i32)(max_h + r); + y = (base - font.glyph.bitmap_top + r) + max_h; foreach(c; 0 .. bmp.width) { x = max_w + c; - u64 offset = (y*dimension + x) * 4; + u64 offset = (y*atlas_dim.y + x) * 4; u8 p_b = bmp.buffer[offset+0]; u8 p_g = bmp.buffer[offset+1]; @@ -191,13 +205,13 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ case FT_PIXEL_MODE_MONO: { u32 pitch = bmp.pitch; - u8* buf = bmp.buffer; - i32 x, y; + u8* buf = bmp.buffer; + u64 x, y; foreach(r; 0 .. bmp.rows) { u8 bits = 0; u8* buf_ptr = buf; - y = cast(i32)(max_h + r); + y = (base - font.glyph.bitmap_top + r) + max_h; foreach(c; 0 .. bmp.width) { if((c & 7) == 0) @@ -207,7 +221,7 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ } x = max_w + c; - u64 offset = (y*dimension + x) * 4; + u64 offset = (y*atlas_dim.y + x) * 4; abuf.data[offset .. offset+4] = [255, 255, 255, bits & 0x80 ? 255 : 0]; bits <<= 1; @@ -218,14 +232,15 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ } break; case FT_PIXEL_MODE_GRAY: { - i32 x, y; + u64 x, y; foreach(r; 0 .. bmp.rows) { - y = cast(i32)(max_h + r); + y = (base - font.glyph.bitmap_top + r) + max_h; + Logf("y %s %s %s %s", baseline - font.glyph.bitmap_top +r, baseline, font.glyph.bitmap_top, r); foreach(c; 0 .. bmp.width) { x = max_w + c; - u64 offset = (y*dimension + x) * 4; + u64 offset = (y*atlas_dim.y + x) * 4; abuf.data[offset .. offset+4] = [255, 255, 255, bmp.buffer[r*bmp.pitch + c]]; } @@ -248,17 +263,16 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ g.ch = cast(dchar)char_code; g.advance = cast(f32)(glyph.advance.x >> 6); g.plane_left = left; - g.plane_right = g.plane_left + width; - g.plane_top = cast(f32)(bbox_ymax) - top; - g.plane_bottom = g.plane_top + height; + g.plane_right = g.plane_left + bmp.width; + g.plane_top = 0.0; + g.plane_bottom = line_height; g.atlas_top = max_h; g.atlas_left = max_w; - g.atlas_bottom = max_h + bmp.rows; + g.atlas_bottom = max_h + line_height; g.atlas_right = max_w + bmp.width; - max_w += bmp.width + PADDING; - current_h = bmp.rows > current_h ? bmp.rows : current_h; + max_w += bmp.width + PADDING; count += 1; } @@ -268,3 +282,4 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension, bool y_origin_ } +