asterinas/kernel/comps/console/src/font.rs

175 lines
5.1 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
//! Font types.
use alloc::{boxed::Box, vec::Vec};
use font8x8::UnicodeFonts;
/// A bitmap font.
///
/// Currently it's mainly used to draw texts on the framebuffer console.
#[derive(Debug)]
pub struct BitmapFont {
width: usize,
height: usize,
char_size: usize,
bitmap: Box<[u8]>,
}
impl BitmapFont {
/// Creates a new bitmap font.
///
/// In a bitmap, a one in a given position indicates that the foreground pixel should be drawn
/// at that row and column.
///
/// The font should contain a set of characters, starting from the NUL character. Each
/// character contains `height` rows and each row contains `width` columns. So a row contains
/// `width` bits and occupies `width.div_ceil(u8::BITS as _)` bytes. Note that the least
/// significant bit of a byte represents the smallest column number.
///
/// # Panics
///
/// This method will panic if the bitmap contains no characters or the last character in the
/// bitmap is incomplete.
pub fn new(width: usize, height: usize, bitmap: Box<[u8]>) -> Self {
let row_size = width.div_ceil(u8::BITS as usize);
let char_size = row_size.checked_mul(height).unwrap();
assert_ne!(bitmap.len(), 0);
assert_eq!(bitmap.len() % char_size, 0);
Self {
width,
height,
char_size,
bitmap,
}
}
/// Creates a new bitmap font that represents the basic 8x8 font.
///
/// This is the default font for the framebuffer console.
pub fn new_basic8x8() -> Self {
const CHAR_COUNT: u32 = 0x7F;
const FONT_WIDTH: usize = 8;
const FONT_HEIGHT: usize = 8;
let bitmap = (0..CHAR_COUNT)
.flat_map(|ch| {
font8x8::BASIC_FONTS
.get(char::from_u32(ch).unwrap())
.unwrap()
.into_iter()
})
.collect();
Self::new(FONT_WIDTH, FONT_HEIGHT, bitmap)
}
/// Creates a new bitmap font with the specified `vpitch` value.
///
/// This method is similar to [`BitmapFont::new`], except that each character in the bitmap
/// contains `vpitch` rows. Note that the character height is still `height`, so the remaining
/// rows in the bitmap will be ignored.
///
/// # Panics
///
/// This method will panic if `height` is smaller than `vpitch`.
///
/// Besides, this method will panic for the same reasons specified in [`BitmapFont::new`].
pub fn new_with_vpitch(
width: usize,
height: usize,
vpitch: usize,
mut bitmap: Vec<u8>,
) -> Self {
if height == vpitch {
return Self::new(width, height, bitmap.into_boxed_slice());
}
assert!(height < vpitch);
let row_size = width.div_ceil(u8::BITS as usize);
let char_size_old = row_size.checked_mul(vpitch).unwrap();
let char_size_new = row_size.checked_mul(height).unwrap();
assert_ne!(bitmap.len(), 0);
assert_eq!(bitmap.len() % char_size_old, 0);
let mut old_pos = char_size_old;
let mut new_pos = char_size_new;
while old_pos < bitmap.len() {
bitmap.copy_within(old_pos..old_pos + char_size_new, new_pos);
old_pos += char_size_old;
new_pos += char_size_new;
}
bitmap.truncate(new_pos);
Self::new(width, height, bitmap.into_boxed_slice())
}
/// Returns the width of the font.
pub fn width(&self) -> usize {
self.width
}
/// Returns the height of the font.
pub fn height(&self) -> usize {
self.height
}
/// Returns the bitmap of the specified character in the font.
///
/// This method will return [`None`] if the font does not contain a bitmap for the specified
/// character.
pub fn char(&self, ch: u8) -> Option<BitmapChar<'_>> {
let pos = (ch as usize) * self.char_size;
let data = self.bitmap.get(pos..pos + self.char_size)?;
Some(BitmapChar {
font: self,
char_data: data,
})
}
}
/// A bitmap of a character in a [`BitmapFont`].
#[derive(Debug)]
pub struct BitmapChar<'a> {
font: &'a BitmapFont,
char_data: &'a [u8],
}
impl<'a> BitmapChar<'a> {
/// Iterates over the rows of the character bitmap.
pub fn rows(&self) -> impl Iterator<Item = BitmapCharRow<'a>> {
let row_size = self.font.width.div_ceil(u8::BITS as usize);
self.char_data
.chunks_exact(row_size)
.map(|chunk| BitmapCharRow {
font: self.font,
row_data: chunk,
})
}
}
/// A bitmap of a row in a [`BitmapChar`].
#[derive(Debug)]
pub struct BitmapCharRow<'a> {
font: &'a BitmapFont,
row_data: &'a [u8],
}
impl BitmapCharRow<'_> {
/// Iterates over the bits (i.e., the columns) of the row bitmap.
pub fn bits(&self) -> impl Iterator<Item = bool> + '_ {
(0..self.font.width).map(|i| {
let nbyte = i / (u8::BITS as usize);
let nbit = i % (u8::BITS as usize);
self.row_data[nbyte] & (1 << nbit) != 0
})
}
}