Files
MontaukOS/programs/include/gui/font.hpp
T

135 lines
5.0 KiB
C++

/*
* font.hpp
* MontaukOS text rendering — TrueType with bitmap fallback
* Copyright (c) 2025 Daniel Hammer
*/
#pragma once
#include "gui/gui.hpp"
#include "gui/framebuffer.hpp"
#include "gui/truetype.hpp"
namespace gui {
static constexpr int FONT_WIDTH = 8;
static constexpr int FONT_HEIGHT = 16;
// Defined by programs that ship the built-in VGA fallback. Keep the symbol
// weak so Canvas remains usable by small apps that intentionally rely only on
// TrueType fonts, while login/desktop can recover when TrueType setup fails.
extern const uint8_t font_data[256 * 16] __attribute__((weak));
// The bitmap fallback font is Code Page 437. Map a Unicode codepoint to its
// CP437 cell ('?' when it has none) so UTF-8 text still draws sensibly,
// including the box-drawing characters terminal programs use.
inline uint8_t cp437_index(uint32_t cp) {
if (cp < 0x80) return (uint8_t)cp;
static constexpr uint16_t high[128] = {
0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,
0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5,
0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,
0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192,
0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,
0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB,
0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510,
0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,
0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567,
0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,
0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580,
0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,
0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229,
0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,
0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0,
};
for (int i = 0; i < 128; i++)
if (high[i] == cp) return (uint8_t)(0x80 + i);
return '?';
}
// Dynamic font height: TTF line height or 16 (bitmap fallback)
inline int system_font_height() {
if (fonts::system_font && fonts::system_font->valid)
return fonts::system_font->get_line_height(fonts::UI_SIZE);
return FONT_HEIGHT;
}
// Dynamic mono font cell dimensions
inline int mono_cell_width() {
if (fonts::mono && fonts::mono->valid) {
// Monospace: all glyphs have the same advance
GlyphCache* gc = fonts::mono->get_cache(fonts::TERM_SIZE);
CachedGlyph* g = fonts::mono->get_glyph(gc, 'M');
if (g) return g->advance;
}
return FONT_WIDTH;
}
inline int mono_cell_height() {
if (fonts::mono && fonts::mono->valid)
return fonts::mono->get_line_height(fonts::TERM_SIZE);
return FONT_HEIGHT;
}
inline void draw_char(Framebuffer& fb, int x, int y, char c, Color fg) {
const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT];
for (int row = 0; row < FONT_HEIGHT; row++) {
uint8_t bits = glyph[row];
for (int col = 0; col < FONT_WIDTH; col++) {
if (bits & (0x80 >> col)) {
fb.put_pixel(x + col, y + row, fg);
}
}
}
}
inline void draw_char_bg(Framebuffer& fb, int x, int y, char c, Color fg, Color bg) {
const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT];
for (int row = 0; row < FONT_HEIGHT; row++) {
uint8_t bits = glyph[row];
for (int col = 0; col < FONT_WIDTH; col++) {
if (bits & (0x80 >> col)) {
fb.put_pixel(x + col, y + row, fg);
} else {
fb.put_pixel(x + col, y + row, bg);
}
}
}
}
inline void draw_text(Framebuffer& fb, int x, int y, const char* text, Color fg) {
if (fonts::system_font && fonts::system_font->valid) {
fonts::system_font->draw(fb, x, y, text, fg, fonts::UI_SIZE);
return;
}
for (const char* p = text; *p; x += FONT_WIDTH) {
draw_char(fb, x, y, (char)cp437_index(montauk::utf8::next(&p)), fg);
}
}
inline void draw_text_bg(Framebuffer& fb, int x, int y, const char* text, Color fg, Color bg) {
if (fonts::system_font && fonts::system_font->valid) {
fonts::system_font->draw_bg(fb, x, y, text, fg, bg, fonts::UI_SIZE);
return;
}
for (const char* p = text; *p; x += FONT_WIDTH) {
draw_char_bg(fb, x, y, (char)cp437_index(montauk::utf8::next(&p)), fg, bg);
}
}
// Width of the first `len` bytes of a UTF-8 string in the UI font.
inline int text_width_n(const char* text, int len) {
if (fonts::system_font && fonts::system_font->valid)
return fonts::system_font->measure_text_n(text, len, fonts::UI_SIZE);
return montauk::utf8::count(text, len) * FONT_WIDTH;
}
inline int text_width(const char* text) {
if (fonts::system_font && fonts::system_font->valid) {
return fonts::system_font->measure_text(text, fonts::UI_SIZE);
}
return montauk::utf8::count(text) * FONT_WIDTH;
}
} // namespace gui