175 lines
5.1 KiB
Rust
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
|
|
})
|
|
}
|
|
}
|