#include #include #include SharedPtr s_default_font = {}; SharedPtr s_default_bold_font = {}; constexpr static int BYTES_PER_PIXEL = (int)sizeof(ui::Color); namespace ui { Result> Font::load(const os::Path& path) { auto font = TRY(make_shared()); auto file = TRY(os::File::open(path, os::File::ReadOnly)); TRY(file->read_typed(font->m_psf_header)); if (font->m_psf_header.magic != PSF_FONT_MAGIC) { os::eprintln("ui::Font::load(%s) failed: font magic does not match PSF2 magic", path.name().chars()); return err(ENOTSUP); } if (font->m_psf_header.version != 0) { os::eprintln("ui::Font::load(%s) failed: font version is unsupported", path.name().chars()); return err(ENOTSUP); } if (font->m_psf_header.flags) { os::eprintln("ui::Font::load(%s) warning: font has a unicode table, which we're ignoring", path.name().chars()); // todo(); // Font has a unicode table, oh no! } font->m_font_data = TRY(file->read_all()); // Read the rest of the file into the font data buffer. return font; } Result> Font::load_builtin(StringView name, FontWeight weight) { auto path = TRY(String::format("/usr/share/fonts/%s-%s.psf"_sv, name.chars(), weight == FontWeight::Bold ? "Bold" : "Regular")); return load(path.view()); } SharedPtr Font::default_font() { if (s_default_font) return s_default_font; s_default_font = load("/usr/share/fonts/Tamsyn-Regular.psf").release_value(); return s_default_font; } SharedPtr Font::default_bold_font() { if (s_default_bold_font) return s_default_bold_font; s_default_bold_font = load("/usr/share/fonts/Tamsyn-Bold.psf").release_value(); return s_default_bold_font; } void Font::render(wchar_t codepoint, ui::Color color, ui::Canvas& canvas) { check(canvas.width == m_psf_header.width && canvas.height == m_psf_header.height); const wchar_t str[] = { codepoint, 0 }; render(str, color, canvas); } void Font::render(const wchar_t* text, ui::Color color, ui::Canvas& canvas) { usize len = wcslen(text); int height = m_psf_header.height; int width = m_psf_header.width; int last_char_width = width; if (canvas.width < (m_psf_header.width * static_cast(len))) { len = (canvas.width / width) + 1; last_char_width = canvas.width % width; } if (canvas.height < height) height = canvas.height; const int bytes_per_line = (m_psf_header.width + 7) / 8; for (usize i = 0; i < len; i++) { if (i + 1 == len) width = last_char_width; wchar_t codepoint = text[i]; u8* glyph = m_font_data.data() + (codepoint > 0 && codepoint < (wchar_t)m_psf_header.numglyph ? codepoint : 0) * m_psf_header.bytesperglyph; u32 offset = (u32)i * (m_psf_header.width + 1) * BYTES_PER_PIXEL; for (int y = 0; y < height; y++) { u32 line = offset; int mask = 1 << (m_psf_header.width - 1); for (int x = 0; x < m_psf_header.width; x++) { if (*((u32*)glyph) & mask) *(u32*)(canvas.ptr + line) = color.raw; mask >>= 1; line += BYTES_PER_PIXEL; } glyph += bytes_per_line; offset += canvas.stride * BYTES_PER_PIXEL; } } } }