use crate::font::{FontLineMetrics, FontMetrics, GlyphInfo};
use crate::renderer::Renderer;
use crate::widget::{Color, Rect};
pub struct BitmapFont {
pub glyph_width: u8,
pub glyph_height: u8,
pub scale: u8,
pub data: &'static [u8],
}
impl BitmapFont {
pub fn scaled_width(&self) -> i32 {
self.glyph_width as i32 * self.scale as i32
}
pub fn scaled_height(&self) -> i32 {
self.glyph_height as i32 * self.scale as i32
}
pub fn draw_char(&self, renderer: &mut dyn Renderer, x: i32, y: i32, ch: char, color: Color) {
let idx = if (0x20..=0x7E).contains(&(ch as u32)) {
(ch as u32 - 0x20) as usize
} else {
return; };
let bits_per_glyph = self.glyph_width as usize * self.glyph_height as usize;
let bit_offset = idx * bits_per_glyph;
let s = self.scale as i32;
for row in 0..self.glyph_height as usize {
for col in 0..self.glyph_width as usize {
let bit = bit_offset + row * self.glyph_width as usize + col;
let byte_idx = bit / 8;
let bit_idx = 7 - (bit % 8); if byte_idx < self.data.len() && (self.data[byte_idx] >> bit_idx) & 1 != 0 {
renderer.fill_rect(
Rect {
x: x + col as i32 * s,
y: y + row as i32 * s,
width: s,
height: s,
},
color,
);
}
}
}
}
pub fn draw_str(&self, renderer: &mut dyn Renderer, x: i32, y: i32, text: &str, color: Color) {
let advance = self.scaled_width() + self.scale as i32;
let mut cx = x;
for ch in text.chars() {
self.draw_char(renderer, cx, y, ch, color);
cx += advance;
}
}
pub fn draw_str_y(
&self,
renderer: &mut dyn Renderer,
x: i32,
y: i32,
text: &str,
color: Color,
) {
let advance = self.scaled_width() + self.scale as i32;
let mut cy = y;
for ch in text.chars() {
self.draw_char(renderer, x, cy, ch, color);
cy += advance;
}
}
}
impl FontMetrics for BitmapFont {
fn glyph_metrics(&self, ch: char) -> Option<GlyphInfo> {
if !(0x20..=0x7e).contains(&(ch as u32)) {
return None;
}
let width = self.scaled_width().max(0) as u16;
let height = self.scaled_height().max(0) as u16;
let advance_px = self.scaled_width() + self.scale as i32;
Some(GlyphInfo {
advance_fp16: (advance_px.max(0) * 16).min(u16::MAX as i32) as u16,
bearing_x: 0,
bearing_y: height.min(i16::MAX as u16) as i16,
width,
height,
})
}
fn line_metrics(&self) -> FontLineMetrics {
let height = self.scaled_height().max(0).min(u16::MAX as i32) as u16;
FontLineMetrics {
line_height: height,
ascent: height.min(i16::MAX as u16) as i16,
descent: 0,
}
}
fn glyph_coverage_row(&self, ch: char, row: u16, x_offset: u16, coverage: &mut [u8]) -> bool {
if !(0x20..=0x7e).contains(&(ch as u32)) {
return false;
}
let scale = self.scale.max(1) as usize;
let source_row = row as usize / scale;
if source_row >= self.glyph_height as usize {
coverage.fill(0);
return true;
}
let glyph_idx = (ch as u32 - 0x20) as usize;
let bits_per_glyph = self.glyph_width as usize * self.glyph_height as usize;
let bit_offset = glyph_idx * bits_per_glyph;
for (offset, alpha) in coverage.iter_mut().enumerate() {
let source_col = (x_offset as usize + offset) / scale;
if source_col >= self.glyph_width as usize {
*alpha = 0;
continue;
}
let bit = bit_offset + source_row * self.glyph_width as usize + source_col;
let byte_idx = bit / 8;
let bit_idx = 7 - (bit % 8);
*alpha = if byte_idx < self.data.len() && (self.data[byte_idx] >> bit_idx) & 1 != 0 {
255
} else {
0
};
}
true
}
}
pub static FONT_6X10: BitmapFont = BitmapFont {
glyph_width: 6,
glyph_height: 10,
scale: 2,
data: &FONT_6X10_DATA,
};
static FONT_6X10_DATA: [u8; 713] = {
*include_bytes!("bitmap_font_6x10.bin")
};