From fabbef437acefb8103b01df2fa65216d42fbc78e Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 15 Nov 2024 09:21:19 -0600 Subject: [PATCH] Fix more font issues --- bootstrap/bloat-buster/font.c | 110 ++++++++++++++++++-------- bootstrap/bloat-buster/gui.c | 44 ++++++----- bootstrap/include/bloat-buster/font.h | 21 ++++- bootstrap/std/os.c | 99 ++++++++++++++++++++++- 4 files changed, 218 insertions(+), 56 deletions(-) diff --git a/bootstrap/bloat-buster/font.c b/bootstrap/bloat-buster/font.c index e90ca1d..aa4d1ec 100644 --- a/bootstrap/bloat-buster/font.c +++ b/bootstrap/bloat-buster/font.c @@ -1,11 +1,12 @@ #include +#define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #include -TextureAtlas font_create_texture_atlas(Arena* arena, String font_path) +TextureAtlas font_create_texture_atlas(Arena* arena, TextureAtlasCreate create) { - auto font_file = file_read(arena, font_path); + auto font_file = file_read(arena, create.font_path); stbtt_fontinfo font_info; if (!stbtt_InitFont(&font_info, font_file.pointer, stbtt_GetFontOffsetForIndex(font_file.pointer, 0))) { @@ -13,55 +14,102 @@ TextureAtlas font_create_texture_atlas(Arena* arena, String font_path) } TextureAtlas result = {}; - u32 char_count = 256; - result.characters = arena_allocate(arena, FontCharacter, char_count); - result.character_height = 64; - result.character_width = result.character_height; - result.height = 1024; + u32 character_count = 256; + result.characters = arena_allocate(arena, FontCharacter, character_count); + result.kerning_tables = arena_allocate(arena, s32, character_count * character_count); + result.height = (u32)sqrtf((f32)(create.text_height * create.text_height * character_count)); result.width = result.height; auto atlas_size = result.width * result.height; result.pointer = arena_allocate(arena, u8, atlas_size); - result.scale = stbtt_ScaleForPixelHeight(&font_info, result.character_height); - - // Starting position in the atlas - u32 x = 0; - u32 y = 0; + auto scale_factor = stbtt_ScaleForPixelHeight(&font_info, create.text_height); - for (u32 i = 0; i < char_count; ++i) + int ascent; + int descent; + int line_gap; + stbtt_GetFontVMetrics(&font_info, &ascent, &descent, &line_gap); + + result.ascent = (u32)roundf(ascent * scale_factor); + result.descent = (u32)roundf(descent * scale_factor); + result.line_gap = (u32)roundf(line_gap * scale_factor); + + u32 x = 0; + u32 y = 0; + u32 max_row_height = 0; + u32 first_character = ' '; + u32 last_character = '~'; + for (auto i = first_character; i <= last_character; ++i) { u32 width; u32 height; - int x_offset; - int y_offset; + int advance; + int left_bearing; auto ch = (u8)i; auto* character = &result.characters[i]; - stbtt_GetCodepointHMetrics(&font_info, ch, (int*)&character->advance, (int*)&character->left_bearing); + stbtt_GetCodepointHMetrics(&font_info, ch, &advance, &left_bearing); - u8* bitmap = stbtt_GetCodepointBitmap(&font_info, 0.0f, result.scale, ch, (int*)&width, (int*)&height, &x_offset, &y_offset); - if (width > 0 && height > 0) + character->advance = (u32)roundf(advance * scale_factor); + character->left_bearing = (u32)roundf(left_bearing * scale_factor); + + u8* bitmap = stbtt_GetCodepointBitmap(&font_info, 0.0f, scale_factor, ch, (int*)&width, (int*)&height, &character->x_offset, &character->y_offset); + auto* kerning_table = result.kerning_tables + i * character_count; + for (u32 j = first_character; j <= last_character; j += 1) { - for (u32 j = 0; j < height; ++j) + auto kerning_advance = stbtt_GetCodepointKernAdvance(&font_info, i, j); + kerning_table[j] = (s32)roundf(kerning_advance * scale_factor); + } + + if (x + width > result.width) + { + y += max_row_height; + max_row_height = height; + x = 0; + } + else + { + max_row_height = MAX(height, max_row_height); + } + + character->x = x; + character->y = y; + character->width = width; + character->height = height; + + print("Drawing codepoint '{c}' ({u32}x{u32}) at ({u32}, {u32}). Offsets: ({s32}, {s32})\n", ch, width, height, x, y, character->x_offset, character->y_offset); + + auto* source = bitmap; + auto* destination = result.pointer; + for (u32 bitmap_y = 0; bitmap_y < height; bitmap_y += 1) + { + for (u32 bitmap_x = 0; bitmap_x < width; bitmap_x += 1) { - for (u32 i = 0; i < width; ++i) + auto source_index = bitmap_y * width + bitmap_x; + auto destination_index = ((height - bitmap_y - 1) + y) * result.width + bitmap_x + x; + destination[destination_index] = source[source_index]; + } + } + + for (u32 bitmap_y = y; bitmap_y < y + height; bitmap_y += 1) + { + for (u32 bitmap_x = x; bitmap_x < x + width; bitmap_x += 1) + { + auto x = result.pointer[bitmap_y * result.width + bitmap_x]; + if (x) { - auto atlas_index = (y + j) * result.width + (x + i ) + 1; - auto bitmap_index = (height - j - 1) * width + i; - assert(atlas_index < atlas_size); - result.pointer[atlas_index] = bitmap[bitmap_index]; + print("[x]"); + } + else + { + print("[ ]"); } } - stbtt_FreeBitmap(bitmap, 0); + print("\n"); } - x += result.character_width; + x += width; - if (x + result.character_width > result.width) - { - x = 0; - y += result.character_height; - } + stbtt_FreeBitmap(bitmap, 0); } return result; diff --git a/bootstrap/bloat-buster/gui.c b/bootstrap/bloat-buster/gui.c index f628033..033b337 100644 --- a/bootstrap/bloat-buster/gui.c +++ b/bootstrap/bloat-buster/gui.c @@ -1134,7 +1134,10 @@ strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf") #else #endif ; - auto texture_atlas = font_create_texture_atlas(arena, font_path); + auto texture_atlas = font_create_texture_atlas(arena, (TextureAtlasCreate) { + .font_path = font_path, + .text_height = 72, + }); auto texture_atlas_image = vk_image_from_texture(device, allocation_callbacks, immediate, physical_device_memory_properties, (TextureMemory) { .pointer = texture_atlas.pointer, .width = texture_atlas.width, @@ -1242,24 +1245,23 @@ strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf") Vec4 color = {1, 1, 1, 1}; static_assert(sizeof(color) == 4 * sizeof(float)); - auto string = strlit("abcdefghi"); + auto string = strlit("Hello world!"); VirtualBuffer(Vertex) vertices = {}; VirtualBuffer(u32) indices = {}; u32 index_offset = 0; - f32 x_offset = initial_width / 2.0f; - f32 y_offset = initial_height / 2.0f; - f32 font_scale = texture_atlas.scale; + auto x_offset = initial_width / 2; + auto y_offset = initial_height / 2; for (u64 i = 0; i < string.length; i += 1, index_offset += 4) { auto ch = string.pointer[i]; - auto character_count_per_row = texture_atlas.width / texture_atlas.character_width; - auto row = ch / character_count_per_row; - auto column = ch % character_count_per_row; + auto* character = &texture_atlas.characters[ch]; auto pos_x = x_offset; - auto pos_y = y_offset; - auto uv_x = column * texture_atlas.character_width; - auto uv_y = row * texture_atlas.character_height; + auto pos_y = y_offset - (character->height + character->y_offset); + auto uv_x = character->x; + auto uv_y = character->y; + auto uv_width = character->width; + auto uv_height = character->height; *vb_add(&vertices, 1) = (Vertex) { .x = pos_x, @@ -1269,24 +1271,24 @@ strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf") .color = color, }; *vb_add(&vertices, 1) = (Vertex) { - .x = pos_x + texture_atlas.character_height, + .x = pos_x + character->width, .y = pos_y, - .uv_x = (f32)(uv_x + texture_atlas.character_height) / texture_atlas.width, + .uv_x = (f32)(uv_x + uv_width) / texture_atlas.width, .uv_y = (f32)uv_y / texture_atlas.width, .color = color, }; *vb_add(&vertices, 1) = (Vertex) { .x = pos_x, - .y = pos_y + texture_atlas.character_height, + .y = pos_y + character->height, .uv_x = (f32)uv_x / texture_atlas.width, - .uv_y = (f32)(uv_y + texture_atlas.character_height) / texture_atlas.width, + .uv_y = (f32)(uv_y + uv_height) / texture_atlas.width, .color = color, }; *vb_add(&vertices, 1) = (Vertex) { - .x = pos_x + texture_atlas.character_height, - .y = pos_y + texture_atlas.character_height, - .uv_x = (f32)(uv_x + texture_atlas.character_height) / texture_atlas.width, - .uv_y = (f32)(uv_y + texture_atlas.character_height) / texture_atlas.width, + .x = pos_x + character->width, + .y = pos_y + character->height, + .uv_x = (f32)(uv_x + uv_width) / texture_atlas.width, + .uv_y = (f32)(uv_y + uv_height) / texture_atlas.width, .color = color, }; @@ -1297,7 +1299,9 @@ strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf") *vb_add(&indices, 1) = index_offset + 3; *vb_add(&indices, 1) = index_offset + 2; - x_offset += texture_atlas.characters[i].advance * font_scale; + auto kerning = (texture_atlas.kerning_tables + ch * 256)[string.pointer[i + 1]]; + print("Advance: {u32}. Kerning: {s32}\n", character->advance, kerning); + x_offset += character->advance + kerning; } auto vertex_buffer_size = sizeof(*vertices.pointer) * vertices.length; diff --git a/bootstrap/include/bloat-buster/font.h b/bootstrap/include/bloat-buster/font.h index aae9805..b45ea9a 100644 --- a/bootstrap/include/bloat-buster/font.h +++ b/bootstrap/include/bloat-buster/font.h @@ -5,17 +5,30 @@ STRUCT(FontCharacter) { u32 advance; u32 left_bearing; + u32 x; + u32 y; + u32 width; + u32 height; + s32 x_offset; + s32 y_offset; }; STRUCT(TextureAtlas) { u8* pointer; FontCharacter* characters; + s32* kerning_tables; u32 width; u32 height; - u32 character_width; - u32 character_height; - f32 scale; + s32 ascent; + s32 descent; + s32 line_gap; }; -EXPORT TextureAtlas font_create_texture_atlas(Arena* arena, String font_path); +STRUCT(TextureAtlasCreate) +{ + String font_path; + u32 text_height; +}; + +EXPORT TextureAtlas font_create_texture_atlas(Arena* arena, TextureAtlasCreate create); diff --git a/bootstrap/std/os.c b/bootstrap/std/os.c index 4a65aea..097e5d4 100644 --- a/bootstrap/std/os.c +++ b/bootstrap/std/os.c @@ -1079,6 +1079,14 @@ void print(const char* format, ...) } } } + else + { + auto character = cast_to(u8, u32, va_arg(args, u32)); + buffer.pointer[buffer_i] = character; + buffer_i += 1; + done = 1; + } + assert(done); } break; case 'f': @@ -1117,7 +1125,96 @@ void print(const char* format, ...) if (is_decimal_digit(*it)) { - trap(); + u8* bit_count_start = it; + while (is_decimal_digit(*it)) + { + it += 1; + } + + u8* bit_count_end = it; + u64 bit_count = parse_decimal(slice_from_pointer_range(u8, (u8*)bit_count_start, (u8*)bit_count_end)); + + typedef enum IntegerFormat : u8 + { + INTEGER_FORMAT_HEXADECIMAL, + INTEGER_FORMAT_DECIMAL, + INTEGER_FORMAT_OCTAL, + INTEGER_FORMAT_BINARY, + } IntegerFormat; + + IntegerFormat format = INTEGER_FORMAT_DECIMAL; + + if (*it == ':') + { + it += 1; + switch (*it) + { + case 'x': + format = INTEGER_FORMAT_HEXADECIMAL; + break; + case 'd': + format = INTEGER_FORMAT_DECIMAL; + break; + case 'o': + format = INTEGER_FORMAT_OCTAL; + break; + case 'b': + format = INTEGER_FORMAT_BINARY; + break; + default: + trap(); + } + + it += 1; + } + + s64 original_value; + switch (bit_count) + { + case 8: + case 16: + case 32: + original_value = va_arg(args, s32); + break; + case 64: + original_value = va_arg(args, s64); + break; + default: + trap(); + } + + auto buffer_slice = s_get_slice(u8, buffer, buffer_i, buffer.length); + + switch (format) + { + case INTEGER_FORMAT_HEXADECIMAL: + { + auto written_characters = format_hexadecimal(buffer_slice, original_value); + buffer_i += written_characters; + } break; + case INTEGER_FORMAT_DECIMAL: + { + u64 value; + if (original_value < 0) + { + buffer_slice.pointer[0] = '-'; + buffer_slice.pointer += 1; + buffer_slice.length -= 1; + buffer_i += 1; + value = (u64)(-(original_value - (original_value == INT64_MIN))) + (original_value == INT64_MIN); + } + else + { + value = (u64)original_value; + } + + auto written_characters = format_decimal(buffer_slice, value); + buffer_i += written_characters; + } break; + case INTEGER_FORMAT_OCTAL: + case INTEGER_FORMAT_BINARY: + trap(); + } } else {