ling-lang
↓ 0 downloadsLing - The Omniglot Systems Language
Keywords systems, polyglot, ai, crypto, game
lingfu install ling-langCode / Files (995)
crates/ling-kernel/src/drivers/font8x8.rs
//! An 8x8, 1bpp bitmap font for the linear framebuffer (`drivers::framebuffer`) --
//! nothing existed for text in pixel-graphics mode before this (the only
//! other font in this kernel, `vga::load_font`, is VGA hardware text-mode's
//! character generator, a completely different consumer that doesn't apply
//! to a linear framebuffer at all).
//!
//! `FONT8X8_BASIC` is the classic public-domain "font8x8_basic" bitmap
//! table (originally released to the public domain / CC0 by its author,
//! widely reproduced across hobby-OS projects) covering printable ASCII
//! 0x20-0x7E, one `u8` per row (bit 0 = leftmost pixel), 8 rows per glyph.
//! Anything outside that range renders as a blank cell rather than
//! panicking or wrapping -- a boot-time/UI text renderer shouldn't crash
//! on a stray byte.
use crate::drivers::framebuffer;
const FIRST: usize = 0x20;
const LAST: usize = 0x7E;
#[rustfmt::skip]
static FONT8X8_BASIC: [[u8; 8]; LAST - FIRST + 1] = [
[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // ' '
[0x18,0x3C,0x3C,0x18,0x18,0x00,0x18,0x00], // '!'
[0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00], // '"'
[0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00], // '#'
[0x0C,0x3E,0x03,0x1E,0x30,0x1F,0x0C,0x00], // '$'
[0x00,0x63,0x33,0x18,0x0C,0x66,0x63,0x00], // '%'
[0x1C,0x36,0x1C,0x6E,0x3B,0x33,0x6E,0x00], // '&'
[0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00], // '''
[0x18,0x0C,0x06,0x06,0x06,0x0C,0x18,0x00], // '('
[0x06,0x0C,0x18,0x18,0x18,0x0C,0x06,0x00], // ')'
[0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00], // '*'
[0x00,0x0C,0x0C,0x3F,0x0C,0x0C,0x00,0x00], // '+'
[0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x06], // ','
[0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00], // '-'
[0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x00], // '.'
[0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00], // '/'
[0x3E,0x63,0x73,0x7B,0x6F,0x67,0x3E,0x00], // '0'
[0x0C,0x0E,0x0C,0x0C,0x0C,0x0C,0x3F,0x00], // '1'
[0x1E,0x33,0x30,0x1C,0x06,0x33,0x3F,0x00], // '2'
[0x1E,0x33,0x30,0x1C,0x30,0x33,0x1E,0x00], // '3'
[0x38,0x3C,0x36,0x33,0x7F,0x30,0x78,0x00], // '4'
[0x3F,0x03,0x1F,0x30,0x30,0x33,0x1E,0x00], // '5'
[0x1C,0x06,0x03,0x1F,0x33,0x33,0x1E,0x00], // '6'
[0x3F,0x33,0x30,0x18,0x0C,0x0C,0x0C,0x00], // '7'
[0x1E,0x33,0x33,0x1E,0x33,0x33,0x1E,0x00], // '8'
[0x1E,0x33,0x33,0x3E,0x30,0x18,0x0E,0x00], // '9'
[0x00,0x0C,0x0C,0x00,0x00,0x0C,0x0C,0x00], // ':'
[0x00,0x0C,0x0C,0x00,0x00,0x0C,0x0C,0x06], // ';'
[0x18,0x0C,0x06,0x03,0x06,0x0C,0x18,0x00], // '<'
[0x00,0x00,0x3F,0x00,0x00,0x3F,0x00,0x00], // '='
[0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00], // '>'
[0x1E,0x33,0x30,0x18,0x0C,0x00,0x0C,0x00], // '?'
[0x3E,0x63,0x7B,0x7B,0x7B,0x03,0x1E,0x00], // '@'
[0x0C,0x1E,0x33,0x33,0x3F,0x33,0x33,0x00], // 'A'
[0x3F,0x66,0x66,0x3E,0x66,0x66,0x3F,0x00], // 'B'
[0x3C,0x66,0x03,0x03,0x03,0x66,0x3C,0x00], // 'C'
[0x1F,0x36,0x66,0x66,0x66,0x36,0x1F,0x00], // 'D'
[0x7F,0x46,0x16,0x1E,0x16,0x46,0x7F,0x00], // 'E'
[0x7F,0x46,0x16,0x1E,0x16,0x06,0x0F,0x00], // 'F'
[0x3C,0x66,0x03,0x03,0x73,0x66,0x7C,0x00], // 'G'
[0x33,0x33,0x33,0x3F,0x33,0x33,0x33,0x00], // 'H'
[0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00], // 'I'
[0x78,0x30,0x30,0x30,0x33,0x33,0x1E,0x00], // 'J'
[0x67,0x66,0x36,0x1E,0x36,0x66,0x67,0x00], // 'K'
[0x0F,0x06,0x06,0x06,0x46,0x66,0x7F,0x00], // 'L'
[0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00], // 'M'
[0x63,0x67,0x6F,0x7B,0x73,0x63,0x63,0x00], // 'N'
[0x1C,0x36,0x63,0x63,0x63,0x36,0x1C,0x00], // 'O'
[0x3F,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00], // 'P'
[0x1E,0x33,0x33,0x33,0x3B,0x1E,0x38,0x00], // 'Q'
[0x3F,0x66,0x66,0x3E,0x36,0x66,0x67,0x00], // 'R'
[0x1E,0x33,0x07,0x0E,0x38,0x33,0x1E,0x00], // 'S'
[0x3F,0x2D,0x0C,0x0C,0x0C,0x0C,0x1E,0x00], // 'T'
[0x33,0x33,0x33,0x33,0x33,0x33,0x3F,0x00], // 'U'
[0x33,0x33,0x33,0x33,0x33,0x1E,0x0C,0x00], // 'V'
[0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00], // 'W'
[0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00], // 'X'
[0x33,0x33,0x33,0x1E,0x0C,0x0C,0x1E,0x00], // 'Y'
[0x7F,0x63,0x31,0x18,0x4C,0x66,0x7F,0x00], // 'Z'
[0x1E,0x06,0x06,0x06,0x06,0x06,0x1E,0x00], // '['
[0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00], // '\'
[0x1E,0x18,0x18,0x18,0x18,0x18,0x1E,0x00], // ']'
[0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00], // '^'
[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF], // '_'
[0x0C,0x0C,0x18,0x00,0x00,0x00,0x00,0x00], // '`'
[0x00,0x00,0x1E,0x30,0x3E,0x33,0x6E,0x00], // 'a'
[0x07,0x06,0x06,0x3E,0x66,0x66,0x3B,0x00], // 'b'
[0x00,0x00,0x1E,0x33,0x03,0x33,0x1E,0x00], // 'c'
[0x38,0x30,0x30,0x3e,0x33,0x33,0x6E,0x00], // 'd'
[0x00,0x00,0x1E,0x33,0x3f,0x03,0x1E,0x00], // 'e'
[0x1C,0x36,0x06,0x0f,0x06,0x06,0x0F,0x00], // 'f'
[0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x1F], // 'g'
[0x07,0x06,0x36,0x6E,0x66,0x66,0x67,0x00], // 'h'
[0x0C,0x00,0x0E,0x0C,0x0C,0x0C,0x1E,0x00], // 'i'
[0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1E], // 'j'
[0x07,0x06,0x66,0x36,0x1E,0x36,0x67,0x00], // 'k'
[0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00], // 'l'
[0x00,0x00,0x33,0x7F,0x7F,0x6B,0x63,0x00], // 'm'
[0x00,0x00,0x1F,0x33,0x33,0x33,0x33,0x00], // 'n'
[0x00,0x00,0x1E,0x33,0x33,0x33,0x1E,0x00], // 'o'
[0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F], // 'p'
[0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78], // 'q'
[0x00,0x00,0x3B,0x6E,0x66,0x06,0x0F,0x00], // 'r'
[0x00,0x00,0x3E,0x03,0x1E,0x30,0x1F,0x00], // 's'
[0x08,0x0C,0x3E,0x0C,0x0C,0x2C,0x18,0x00], // 't'
[0x00,0x00,0x33,0x33,0x33,0x33,0x6E,0x00], // 'u'
[0x00,0x00,0x33,0x33,0x33,0x1E,0x0C,0x00], // 'v'
[0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00], // 'w'
[0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00], // 'x'
[0x00,0x00,0x33,0x33,0x33,0x3E,0x30,0x1F], // 'y'
[0x00,0x00,0x3F,0x19,0x0C,0x26,0x3F,0x00], // 'z'
[0x38,0x0C,0x0C,0x07,0x0C,0x0C,0x38,0x00], // '{'
[0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00], // '|'
[0x07,0x0C,0x0C,0x38,0x0C,0x0C,0x07,0x00], // '}'
[0x6E,0x3B,0x00,0x00,0x00,0x00,0x00,0x00], // '~'
];
pub fn glyph(c: u8) -> &'static [u8; 8] {
static BLANK: [u8; 8] = [0; 8];
let i = c as usize;
if i < FIRST || i > LAST {
return &BLANK;
}
&FONT8X8_BASIC[i - FIRST]
}
/// Draw one 8x8 glyph at pixel position `(x, y)` into the framebuffer's
/// back buffer -- `fg` for set bits, `bg` for clear bits (both 0x00RRGGBB,
/// matching `framebuffer::back_set_pixel`). Call `framebuffer::present()`
/// once after a batch of draws, same discipline as the fill/clear
/// primitives it's built on.
/// Global integer glyph scale for `draw_char`/`draw_str` -- the desktop's
/// "DPI"/large-text setting (see `ui_scale::dpi_font_scale`). 1 = native 8px.
/// It's set only after login (`wm::settings_load`), so the greeter/installer,
/// which draw before that, stay crisp at 1x. Content is clipped to window
/// bodies (`framebuffer` scissor), so 2x text that outgrows a tight box is cut
/// off cleanly rather than corrupting neighbours.
static mut SCALE: u32 = 1;
pub fn set_scale(s: u32) {
unsafe { SCALE = s.clamp(1, 4) };
}
pub fn scale() -> u32 {
unsafe { SCALE }
}
/// Horizontal advance per character at the current scale (8px * scale).
pub fn advance() -> u32 {
8 * scale()
}
// x86_64 draws into the back buffer and `present()`s it; aarch64 (Raspberry Pi)
// has no back-buffer compositor, so text goes straight to the live framebuffer.
#[inline]
fn put_pixel(x: u32, y: u32, color: u32) {
#[cfg(target_arch = "x86_64")]
framebuffer::back_set_pixel(x, y, color);
#[cfg(not(target_arch = "x86_64"))]
framebuffer::set_pixel(x, y, color);
}
#[inline]
fn put_rect(x: u32, y: u32, w: u32, h: u32, color: u32) {
#[cfg(target_arch = "x86_64")]
framebuffer::back_fill_rect(x, y, w, h, color);
#[cfg(not(target_arch = "x86_64"))]
framebuffer::fill_rect(x, y, w, h, color);
}
pub fn draw_char(x: u32, y: u32, c: u8, fg: u32, bg: u32) {
let s = unsafe { SCALE };
if s > 1 {
draw_char_scaled(x, y, c, fg, bg, s);
return;
}
let rows = glyph(c);
for (row, bits) in rows.iter().enumerate() {
for col in 0..8u32 {
let set = (bits >> col) & 1 != 0;
put_pixel(x + col, y + row as u32, if set { fg } else { bg });
}
}
}
/// Draw a byte string left-to-right (8px per character times the current
/// scale), no wrapping (the caller picks `x`/`y` per line -- this is a
/// primitive, not a text-layout engine).
pub fn draw_str(x: u32, y: u32, s: &[u8], fg: u32, bg: u32) {
let adv = advance();
for (i, &c) in s.iter().enumerate() {
draw_char(x + i as u32 * adv, y, c, fg, bg);
}
}
/// Integer-scaled glyph: each source pixel becomes a `scale`x`scale` block.
/// `scale == 1` is identical to `draw_char`. This is real nearest-neighbor
/// upscaling of the 8x8 bitmap -- honestly blocky at higher zoom (the font
/// is an 8x8 atlas, not vector), which is exactly the editor/gallery zoom
/// the desktop offers. `scale == 0` is treated as 1.
pub fn draw_char_scaled(x: u32, y: u32, c: u8, fg: u32, bg: u32, scale: u32) {
let s = scale.max(1);
if s == 1 {
draw_char(x, y, c, fg, bg);
return;
}
let rows = glyph(c);
for (row, bits) in rows.iter().enumerate() {
for col in 0..8u32 {
let set = (bits >> col) & 1 != 0;
put_rect(x + col * s, y + row as u32 * s, s, s, if set { fg } else { bg });
}
}
}