diff --git a/Rsconscript b/Rsconscript index a8e6338..13a01ab 100644 --- a/Rsconscript +++ b/Rsconscript @@ -4,6 +4,7 @@ configure do check_program "grub-mkstandalone" check_program "mformat", on_fail: "Install the mtools package" check_program "xorriso" + check_cfg package: "freetype2", on_fail: "Install libfreetype-dev", use: "freetype" end build do @@ -13,6 +14,8 @@ build do EFI_PART_SIZE = 8 # HOS partition size (MiB) HOS_PART_SIZE = 4 + # Kernel default font size + KFONT_SIZE = 12 class BiosImage < Builder def run(options) @@ -94,14 +97,36 @@ EOF end end + class FontGen < Builder + def run(options) + if @command + finalize_command + else + @sources += ["build/fontgen"] + command = %W[build/fontgen #{@sources.first} #{KFONT_SIZE} #{@target}] + standard_command("FontGen #{@target}", command, {}) + end + end + end + + # FontGen Environment + Environment.new(use: "freetype") do |env| + env["CC"] = "gcc" + env.Program("build/fontgen", glob("fontgen/**/*.c")) + end + + # Kernel Environment Environment.new do |env| env.add_builder(EfiImage) env.add_builder(BiosImage) + env.add_builder(FontGen) env["CCFLAGS"] += %w[-ffreestanding -Wall] env["LDFLAGS"] += %w[-ffreestanding -nostdlib -T src/link.ld] env["LIBS"] += %w[gcc] env["OBJDUMP"] = "i686-elf-objdump" - env.Program("^/hos.elf", glob("src/**/*.{S,c}")) + env.FontGen("^/fontgen/fontgen.c", "font/Hack-Regular.ttf") + env["CPPPATH"] += ["#{env.build_root}/fontgen"] + env.Program("^/hos.elf", glob("src/**/*.{S,c}") + ["^/fontgen/fontgen.c"]) env.depends("#{env.build_root}/hos.elf", "src/link.ld") env.Disassemble("^/hos.elf.txt", "^/hos.elf") env.EfiImage("build/hos-efi.img", %w[^/hos.elf]) diff --git a/font/Hack-Regular.ttf b/font/Hack-Regular.ttf new file mode 100644 index 0000000..097db18 Binary files /dev/null and b/font/Hack-Regular.ttf differ diff --git a/fontgen/fontgen.c b/fontgen/fontgen.c new file mode 100644 index 0000000..02ec470 --- /dev/null +++ b/fontgen/fontgen.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include FT_FREETYPE_H +#include +#include +#include + +#define N_CHARS 128 + +#define round_up_26_6(val) (((val) + 63) >> 6u) + +int max_advance; +int max_top = -9999; +int min_bottom = 9999; +int line_height; +int baseline_offset; + +typedef struct { + int width; + int height; + int top; + int left; + uint8_t * bitmap; +} char_info_t; + +static char_info_t char_infos[N_CHARS]; + +static void load_char(FT_Face face, int char_code) +{ + if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0) + { + return; + } + int advance = round_up_26_6(face->glyph->advance.x); + if (advance > max_advance) + { + max_advance = advance; + } + if ((face->glyph->bitmap.width == 0) || + (face->glyph->bitmap.rows == 0)) + { + return; + } + char_infos[char_code].width = face->glyph->bitmap.width; + char_infos[char_code].height = face->glyph->bitmap.rows; + char_infos[char_code].top = face->glyph->bitmap_top; + if (char_infos[char_code].top > max_top) + { + max_top = char_infos[char_code].top; + } + int bottom = char_infos[char_code].top - char_infos[char_code].height; + if (bottom < min_bottom) + { + min_bottom = bottom; + } + char_infos[char_code].left = face->glyph->bitmap_left; + char_infos[char_code].bitmap = malloc(char_infos[char_code].width * char_infos[char_code].height); + memcpy(char_infos[char_code].bitmap, + face->glyph->bitmap.buffer, + char_infos[char_code].width * char_infos[char_code].height); +} + +static const char * bare_header_name(const char * h_file_name) +{ + const char * p = rindex(h_file_name, '/'); + if (p == NULL) + { + p = h_file_name; + } + else + { + p++; + } + return p; +} + +static char * include_guard_name(const char * h_file_name) +{ + const char * p = bare_header_name(h_file_name); + char * guard_name = malloc(strlen(p) + 1); + strcpy(guard_name, p); + char * m = guard_name; + while (*m != '\0') + { + if ('a' <= *m && *m <= 'z') + { + *m = toupper(*m); + } + else if (('0' <= *m && *m <= '9') || ('A' <= *m && *m <= 'Z')) + { + /* no change */ + } + else + { + *m = '_'; + } + m++; + } + return guard_name; +} + +static void generate_bytes(FILE * file, const uint8_t * bytes, int count) +{ + for (int i = 0; i < count; i++) + { + if (i % 8 == 0) + { + fprintf(file, " "); + } + fprintf(file, "0x%02xu,", bytes[i]); + if ((i + 1) % 8 == 0) + { + fprintf(file, "\n"); + } + else if (i < (count - 1)) + { + fprintf(file, " "); + } + } + if (count % 8 != 0) + { + fprintf(file, "\n"); + } +} + +static void generate(const char * c_file_name) +{ + char * h_file_name = malloc(strlen(c_file_name) + 1); + strcpy(h_file_name, c_file_name); + h_file_name[strlen(h_file_name) - 1] = 'h'; + char * guard = include_guard_name(h_file_name); + + FILE * h_file = fopen(h_file_name, "wb"); + fprintf(h_file, "#ifndef %s\n", guard); + fprintf(h_file, "#define %s\n\n", guard); + fprintf(h_file, "#include \n"); + fprintf(h_file, "typedef struct {\n int width;\n int height;\n int top;\n int left;\n const uint8_t * bitmap;\n} fontgen_char_info_t;\n"); + fprintf(h_file, "typedef struct {\n int line_height;\n int advance;\n int baseline_offset;\n const fontgen_char_info_t ** char_infos;\n} fontgen_font_t;\n"); + fprintf(h_file, "extern const fontgen_font_t kfont;\n"); + fprintf(h_file, "#endif\n"); + fclose(h_file); + + FILE * c_file = fopen(c_file_name, "wb"); + fprintf(c_file, "#include \"%s\"\n", bare_header_name(h_file_name)); + fprintf(c_file, "#include \n"); + for (int i = 0; i < N_CHARS; i++) + { + if (char_infos[i].width > 0) + { + fprintf(c_file, "static const uint8_t char_bitmap_%d[] = {\n", i); + generate_bytes(c_file, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height); + fprintf(c_file, "};\n"); + } + fprintf(c_file, "static const fontgen_char_info_t char_%d = {\n", i); + fprintf(c_file, " %d,\n", char_infos[i].width); + fprintf(c_file, " %d,\n", char_infos[i].height); + fprintf(c_file, " %d,\n", char_infos[i].top); + fprintf(c_file, " %d,\n", char_infos[i].left); + if (char_infos[i].width > 0) + { + fprintf(c_file, " char_bitmap_%d,\n", i); + } + else + { + fprintf(c_file, " NULL,\n"); + } + fprintf(c_file, "};\n\n"); + } + fprintf(c_file, "const fontgen_char_info_t * char_infos[] = {\n"); + for (int i = 0; i < N_CHARS; i++) + { + fprintf(c_file, " &char_%d,\n", i); + } + fprintf(c_file, "};\n"); + fprintf(c_file, "const fontgen_font_t kfont = {\n"); + fprintf(c_file, " %d,\n", line_height); + fprintf(c_file, " %d,\n", max_advance); + fprintf(c_file, " %d,\n", baseline_offset); + fprintf(c_file, " char_infos,\n"); + fprintf(c_file, "};\n"); + fclose(c_file); +} + +int main(int argc, char * argv[]) +{ + /* Expect: font file, size, out file */ + if (argc != 4) + { + fprintf(stderr, "Incorrect arguments\n"); + return 1; + } + const char * font_file = argv[1]; + int size = atoi(argv[2]); + const char * out_file = argv[3]; + + FT_Library ft_library; + if (FT_Init_FreeType(&ft_library) != 0) + { + fprintf(stderr, "Could not initialize freetype\n"); + return 2; + } + + FT_Face face; + if (FT_New_Face(ft_library, font_file, 0, &face) != 0) + { + fprintf(stderr, "Could not open %s\n", font_file); + return 3; + } + + FT_Set_Pixel_Sizes(face, 0, size); + + for (int i = 0; i < N_CHARS; i++) + { + load_char(face, i); + } + line_height = round_up_26_6(face->size->metrics.height); + baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom; + + generate(out_file); + + return 0; +}