From 69f5cd1a76ce0ee4f61201a5cae460664a5d8ca9 Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 27 Aug 2025 07:02:44 +1000 Subject: [PATCH] fix freetype font loading --- fonts.d | 125 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 22 deletions(-) diff --git a/fonts.d b/fonts.d index 7e5bb88..54641d6 100644 --- a/fonts.d +++ b/fonts.d @@ -88,6 +88,19 @@ CloseFont(FontFace font) } } +i32 +Float26(f32 f) +{ + return cast(i32)round(cast(f32)(1 << 6) * f); +} + +u8 +DeMultiply(u8 color, u8 alpha) +{ + u32 v = cast(u32)(255.0 * cast(f32)(color) / cast(f32)(alpha + f32.epsilon) + 0.5); + return v > 255 ? 255 : cast(u8)v; +} + FontAtlasBuf CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) { @@ -105,8 +118,6 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) // 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); @@ -116,8 +127,13 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) u32 current_h = 0; u32 count = 0; + i32 font_size = Float26(size); + + FT_Library_SetLcdFilter(FT_LIB, FT_LCD_FILTER_NONE); + foreach(FT_ULong char_code; 0 .. 0x7F) { + FT_Set_Char_Size(font, font_size, 0, 0, 0); FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); if (res != 0) { @@ -139,16 +155,16 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) count += 1; } - max_w = 0; - max_h = 0; + const u32 PADDING = 2; + + max_w = PADDING; + max_h = PADDING; current_h = 0; count = 0; - u32 font_w = font.size.metrics.x_ppem; - u32 font_h = font.size.metrics.y_ppem; - foreach(FT_ULong char_code; 0 .. 0x7F) { + FT_Set_Char_Size(font, font_size, 0, 0, 0); FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); if (res != 0) { @@ -162,24 +178,88 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) if (max_w + bmp.rows > dimension) { - max_h += current_h; - max_w = 0; + max_h += cast(u32)(size) + PADDING; + max_w = PADDING; } - i32 x, y; - foreach(r; 0 .. bmp.rows) + switch(bmp.pixel_mode) { - y = cast(i32)(max_h + r); - foreach(c; 0 .. bmp.width) - { - x = max_w + c; - u64 offset = (y*dimension + x) * 4; + case FT_PIXEL_MODE_BGRA: + { + i32 x, y; + foreach(r; 0 .. bmp.rows) + { + y = cast(i32)(max_h + r); + foreach(c; 0 .. bmp.width) + { + x = max_w + 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]; - } + u8 p_b = bmp.buffer[offset+0]; + u8 p_g = bmp.buffer[offset+1]; + u8 p_r = bmp.buffer[offset+2]; + u8 p_a = bmp.buffer[offset+3]; + + atlas.data[offset+0] = DeMultiply(p_r, p_a); + atlas.data[offset+1] = DeMultiply(p_b, p_a); + atlas.data[offset+2] = DeMultiply(p_g, p_a); + atlas.data[offset+3] = p_a; + } + } + } break; + case FT_PIXEL_MODE_MONO: + { + u32 pitch = bmp.pitch; + u8* buf = bmp.buffer; + i32 x, y; + foreach(r; 0 .. bmp.rows) + { + u8 bits = 0; + u8* buf_ptr = buf; + y = cast(i32)(max_h + r); + foreach(c; 0 .. bmp.width) + { + if ((c & 7) == 0) + { + bits = *buf_ptr; + buf_ptr += 1; + } + + x = max_w + 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] = (bits & 0x80) ? 255 : 0; + + bits <<= 1; + } + + buf += pitch; + } + } break; + case FT_PIXEL_MODE_GRAY: + { + i32 x, y; + foreach(r; 0 .. bmp.rows) + { + y = cast(i32)(max_h + r); + foreach(c; 0 .. bmp.width) + { + x = max_w + 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]; + } + } + } break; + default: + assert(false, "Unknown pixel_mode value"); + break; } Glyph* g = atlas.atlas.glyphs.ptr + char_code; @@ -196,7 +276,7 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) g.atlas_bottom = max_h + bmp.rows; g.atlas_right = max_w + bmp.width; - max_w += bmp.width; + max_w += bmp.width + PADDING; current_h = bmp.rows > current_h ? bmp.rows : current_h; count += 1; @@ -206,3 +286,4 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) return atlas; } +