feat(display): DrawText with built-in Spleen 8x16 font (BSD-2)

This commit is contained in:
2026-10-27 16:33:26 +01:00
parent 8587661684
commit 64ec5df379
8 changed files with 290 additions and 2 deletions

View File

@@ -48,3 +48,78 @@ impl Dimension {
self.0
}
}
/// `DrawText`, shared by the display server and `libkos::display`: up to
/// `TEXT_BYTES` characters at a pixel position, each cell `fg` on `bg`.
/// Words: `[TEXT | len << 8 | fg << 16, x | y << 16 | bg << 32, bytes 0..8, 8..16]`.
pub mod text {
use super::{Color, ScreenCoord};
pub const TEXT: usize = 4;
pub const TEXT_BYTES: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Text {
pub x: ScreenCoord,
pub y: ScreenCoord,
pub fg: Color,
pub bg: Color,
pub bytes: [u8; TEXT_BYTES],
pub len: usize,
}
impl Text {
/// `s` past `TEXT_BYTES` is cut off.
pub fn new(x: ScreenCoord, y: ScreenCoord, fg: Color, bg: Color, s: &[u8]) -> Self {
let len = s.len().min(TEXT_BYTES);
let mut bytes = [0u8; TEXT_BYTES];
bytes[..len].copy_from_slice(&s[..len]);
Self { x, y, fg, bg, bytes, len }
}
pub fn chars(&self) -> &[u8] {
&self.bytes[..self.len.min(TEXT_BYTES)]
}
pub fn words(&self) -> [usize; 4] {
let (lo, hi) = self.bytes.as_chunks::<8>().0.split_at(1);
[
TEXT | self.len << 8 | (self.fg.raw() as usize) << 16,
self.x.raw() as usize | (self.y.raw() as usize) << 16 | (self.bg.raw() as usize) << 32,
u64::from_le_bytes(lo[0]) as usize,
u64::from_le_bytes(hi[0]) as usize,
]
}
pub fn from_words(w: [usize; 4]) -> Option<Self> {
let len = (w[0] >> 8) & 0xFF;
if w[0] & 0xFF != TEXT || len > TEXT_BYTES {
return None;
}
let mut bytes = [0u8; TEXT_BYTES];
for (b, word) in bytes.as_chunks_mut::<8>().0.iter_mut().zip(&w[2..]) {
*b = (*word as u64).to_le_bytes();
}
Some(Self {
x: ScreenCoord::new((w[1] & 0xFFFF) as u16),
y: ScreenCoord::new(((w[1] >> 16) & 0xFFFF) as u16),
fg: Color::from_raw((w[0] >> 16) as u32),
bg: Color::from_raw((w[1] >> 32) as u32),
bytes,
len,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn text_round_trips() {
let t = Text::new(ScreenCoord::new(1272), ScreenCoord::new(784), Color::rgb(1, 2, 3), Color::rgb(4, 5, 6), b"$ ls -la /home/x");
assert_eq!(Text::from_words(t.words()), Some(t));
assert_eq!(Text::new(t.x, t.y, t.fg, t.bg, &[b'a'; 40]).len, TEXT_BYTES);
}
}
}