use core::marker::PhantomData;
use embedded_graphics::{prelude::*, style::TextStyle};
#[derive(Copy, Clone, Debug)]
pub struct Glyph<F: Font> {
_font: PhantomData<F>,
char_idx: u32,
}
impl<F> Glyph<F>
where
F: Font,
{
#[inline]
pub fn new(c: char) -> Self {
let char_offset = F::char_offset(c);
let char_per_row = F::FONT_IMAGE_WIDTH / F::CHARACTER_SIZE.width;
let char_x = char_offset % char_per_row * F::CHARACTER_SIZE.width;
let char_y = char_offset / char_per_row * F::CHARACTER_SIZE.height;
Self {
_font: PhantomData,
char_idx: char_x + char_y * F::FONT_IMAGE_WIDTH,
}
}
#[inline]
#[must_use]
pub fn point(&self, p: Point) -> bool {
let bitmap_bit_index = self.char_idx + p.x as u32 + p.y as u32 * F::FONT_IMAGE_WIDTH;
let bitmap_byte = bitmap_bit_index / 8;
let bitmap_bit = bitmap_bit_index % 8;
F::FONT_IMAGE[bitmap_byte as usize] & (0x80 >> bitmap_bit) != 0
}
}
#[derive(Clone, Debug)]
pub struct StyledCharacterIterator<C, F>
where
C: PixelColor,
F: Font + Copy,
{
character: Glyph<F>,
style: TextStyle<C, F>,
pos: Point,
char_walk: Point,
max_x: i32,
}
impl<C, F> StyledCharacterIterator<C, F>
where
C: PixelColor,
F: Font + Copy,
{
#[inline]
#[must_use]
pub fn new(character: char, pos: Point, style: TextStyle<C, F>) -> Self {
Self {
character: Glyph::new(character),
style,
pos,
char_walk: Point::zero(),
max_x: F::char_width(character) as i32 - 1,
}
}
}
impl<C, F> Iterator for StyledCharacterIterator<C, F>
where
C: PixelColor,
F: Font + Copy,
{
type Item = Pixel<C>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.char_walk.y >= F::CHARACTER_SIZE.height as i32 {
break None;
}
let pos = self.char_walk;
if pos.x < self.max_x {
self.char_walk.x += 1;
} else {
self.char_walk.x = 0;
self.char_walk.y += 1;
}
let color = if self.character.point(pos) {
self.style.text_color
} else {
self.style.background_color
};
if let Some(color) = color {
let p = self.pos + pos;
break Some(Pixel(p, color));
}
}
}
}
#[cfg(test)]
mod test {
use super::StyledCharacterIterator;
use embedded_graphics::{
fonts::Font6x8, mock_display::MockDisplay, pixelcolor::BinaryColor, prelude::*,
style::TextStyleBuilder,
};
#[test]
fn transparent_char() {
let mut display = MockDisplay::new();
let style = TextStyleBuilder::new(Font6x8)
.background_color(BinaryColor::On)
.build();
StyledCharacterIterator::new('A', Point::zero(), style)
.draw(&mut display)
.unwrap();
assert_eq!(
display,
MockDisplay::from_pattern(&[
"# ## ",
" ### # ",
" ### # ",
" # ",
" ### # ",
" ### # ",
" ### # ",
"###### "
])
);
}
}