generate a built-in kernel font

This commit is contained in:
Josh Holtrop 2020-10-20 00:34:53 -04:00
parent e22b90b768
commit 610f30fae9
3 changed files with 250 additions and 1 deletions

View File

@ -4,6 +4,7 @@ configure do
check_program "grub-mkstandalone" check_program "grub-mkstandalone"
check_program "mformat", on_fail: "Install the mtools package" check_program "mformat", on_fail: "Install the mtools package"
check_program "xorriso" check_program "xorriso"
check_cfg package: "freetype2", on_fail: "Install libfreetype-dev", use: "freetype"
end end
build do build do
@ -13,6 +14,8 @@ build do
EFI_PART_SIZE = 8 EFI_PART_SIZE = 8
# HOS partition size (MiB) # HOS partition size (MiB)
HOS_PART_SIZE = 4 HOS_PART_SIZE = 4
# Kernel default font size
KFONT_SIZE = 12
class BiosImage < Builder class BiosImage < Builder
def run(options) def run(options)
@ -94,14 +97,36 @@ EOF
end end
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>#{@target}<reset>", 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| Environment.new do |env|
env.add_builder(EfiImage) env.add_builder(EfiImage)
env.add_builder(BiosImage) env.add_builder(BiosImage)
env.add_builder(FontGen)
env["CCFLAGS"] += %w[-ffreestanding -Wall] env["CCFLAGS"] += %w[-ffreestanding -Wall]
env["LDFLAGS"] += %w[-ffreestanding -nostdlib -T src/link.ld] env["LDFLAGS"] += %w[-ffreestanding -nostdlib -T src/link.ld]
env["LIBS"] += %w[gcc] env["LIBS"] += %w[gcc]
env["OBJDUMP"] = "i686-elf-objdump" 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.depends("#{env.build_root}/hos.elf", "src/link.ld")
env.Disassemble("^/hos.elf.txt", "^/hos.elf") env.Disassemble("^/hos.elf.txt", "^/hos.elf")
env.EfiImage("build/hos-efi.img", %w[^/hos.elf]) env.EfiImage("build/hos-efi.img", %w[^/hos.elf])

BIN
font/Hack-Regular.ttf Normal file

Binary file not shown.

224
fontgen/fontgen.c Normal file
View File

@ -0,0 +1,224 @@
#include <stdlib.h>
#include <stdint.h>
#include <ft2build.h>
#include <stdio.h>
#include FT_FREETYPE_H
#include <string.h>
#include <strings.h>
#include <ctype.h>
#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 <stdint.h>\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 <stddef.h>\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;
}