use embedded_graphics::{
draw_target::DrawTarget,
geometry::Point,
pixelcolor::PixelColor,
Drawable,
};
use crate::renderer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Anchor {
TopLeft,
TopCenter,
TopRight,
MiddleLeft,
MiddleCenter,
MiddleRight,
BottomLeft,
BottomCenter,
BottomRight,
}
impl Default for Anchor {
fn default() -> Self {
Anchor::TopLeft
}
}
pub trait WithAnchor: Sized {
fn with_anchor(self, anchor: Anchor) -> Self;
}
pub struct Font {
pub lookup_table: &'static [GlyphEntry],
pub data: &'static [u8],
pub ascii_begin: u32,
pub ascii_end: u32,
pub max_height: u8,
#[allow(dead_code)]
pub proportional_width: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlyphEntry {
pub width: u8,
pub offset: u16,
}
pub struct Text<'a, C> {
text: &'a str,
position: Point,
font: &'a Font,
color: C,
anchor: Anchor,
tracking: i32,
}
pub struct Character<'a, C> {
ch: char,
position: Point,
font: &'a Font,
color: C,
anchor: Anchor,
}
impl Font {
pub const fn new(
lookup_table: &'static [GlyphEntry],
data: &'static [u8],
ascii_begin: u32,
ascii_end: u32,
max_height: u8,
proportional_width: bool,
) -> Self {
Self {
lookup_table,
data,
ascii_begin,
ascii_end,
max_height,
proportional_width,
}
}
#[inline]
pub fn get_glyph(&self, c: char) -> Option<&GlyphEntry> {
let code = c as u32;
if code >= self.ascii_begin && code <= self.ascii_end {
let index = (code - self.ascii_begin) as usize;
self.lookup_table.get(index)
} else {
None
}
}
pub fn glyph_data(&self, entry: &GlyphEntry) -> &[u8] {
let start = entry.offset as usize;
let bytes_per_row = (entry.width as usize + 7) / 8;
let end = start + (bytes_per_row * self.max_height as usize);
&self.data[start..end]
}
pub fn measure_str(&self, text: &str, tracking: i32) -> u32 {
let mut total_width: i32 = 0;
let mut count = 0;
for c in text.chars() {
if let Some(entry) = self.get_glyph(c) {
total_width += entry.width as i32 + tracking;
count += 1;
}
}
if count > 0 && tracking > 0 {
total_width -= tracking;
}
total_width.max(0) as u32
}
}
impl<'a, C> Text<'a, C>
where
C: PixelColor,
{
pub fn new(text: &'a str, position: Point, font: &'a Font, color: C) -> Self {
Self {
text,
position,
font,
color,
anchor: Anchor::TopLeft,
tracking: 0,
}
}
pub fn with_tracking(mut self, tracking: i32) -> Self {
self.tracking = tracking;
self
}
}
impl<'a, C: PixelColor> WithAnchor for Text<'a, C> {
fn with_anchor(mut self, anchor: Anchor) -> Self {
self.anchor = anchor;
self
}
}
impl<'a, C> Character<'a, C>
where
C: PixelColor,
{
pub fn new(ch: char, position: Point, font: &'a Font, color: C) -> Self {
Self {
ch,
position,
font,
color,
anchor: Anchor::TopLeft,
}
}
}
impl<'a, C: PixelColor> WithAnchor for Character<'a, C> {
fn with_anchor(mut self, anchor: Anchor) -> Self {
self.anchor = anchor;
self
}
}
impl<'a, C> Drawable for Text<'a, C>
where
C: PixelColor,
{
type Color = C;
type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
let text_width = self.font.measure_str(self.text, self.tracking) as i32;
let text_height = self.font.max_height as i32;
let (x, y) = match self.anchor {
Anchor::TopLeft => (
self.position.x,
self.position.y
),
Anchor::TopCenter => (
self.position.x - (text_width / 2),
self.position.y
),
Anchor::TopRight => (
self.position.x - text_width,
self.position.y
),
Anchor::MiddleLeft => (
self.position.x,
self.position.y - (text_height / 2)
),
Anchor::MiddleCenter => (
self.position.x - (text_width / 2),
self.position.y - (text_height / 2),
),
Anchor::MiddleRight => (
self.position.x - text_width,
self.position.y - (text_height / 2),
),
Anchor::BottomLeft => (
self.position.x,
self.position.y - text_height
),
Anchor::BottomCenter => (
self.position.x - (text_width / 2),
self.position.y - text_height
),
Anchor::BottomRight => (
self.position.x - text_width,
self.position.y - text_height
),
};
renderer::draw_str(
target,
self.text,
Point::new(x, y),
self.font,
self.color,
self.tracking,
)?;
Ok(())
}
}
impl<'a, C> Drawable for Character<'a, C>
where
C: PixelColor,
{
type Color = C;
type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
let char_width = match self.font.get_glyph(self.ch) {
Some(entry) => entry.width as i32,
None => return Ok(()),
};
let char_height = self.font.max_height as i32;
let (x, y) = match self.anchor {
Anchor::TopLeft => (
self.position.x,
self.position.y
),
Anchor::TopCenter => (
self.position.x - (char_width / 2),
self.position.y
),
Anchor::TopRight => (
self.position.x - char_width,
self.position.y
),
Anchor::MiddleLeft => (
self.position.x,
self.position.y - (char_height / 2)
),
Anchor::MiddleCenter => (
self.position.x - (char_width / 2),
self.position.y - (char_height / 2),
),
Anchor::MiddleRight => (
self.position.x - char_width,
self.position.y - (char_height / 2),
),
Anchor::BottomLeft => (
self.position.x,
self.position.y - char_height
),
Anchor::BottomCenter => (
self.position.x - (char_width / 2),
self.position.y - char_height,
),
Anchor::BottomRight => (
self.position.x - char_width,
self.position.y - char_height
),
};
renderer::draw_char(target, self.ch, Point::new(x, y), self.font, self.color)?;
Ok(())
}
}