update dependencies, initial UTF-8 support
Some checks failed
Rust / build (push) Has been cancelled

This commit is contained in:
Vinzenz Schroeter 2025-01-16 22:28:08 +01:00
commit ad5f1e8abe
9 changed files with 805 additions and 648 deletions

94
src/font_renderer.rs Normal file
View file

@ -0,0 +1,94 @@
use crate::font_renderer::RenderError::{GlyphNotFound, OutOfBounds};
use font_kit::canvas::{Canvas, Format, RasterizationOptions};
use font_kit::error::GlyphLoadingError;
use font_kit::family_name::FamilyName;
use font_kit::font::Font;
use font_kit::hinting::HintingOptions;
use font_kit::properties::Properties;
use font_kit::source::SystemSource;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{vec2f, vec2i};
use servicepoint::{Bitmap, Grid, Origin, Pixels, TILE_SIZE};
use std::sync::Mutex;
pub struct FontRenderer8x8 {
font: Font,
canvas: Mutex<Canvas>,
fallback_char: Option<u32>,
}
#[derive(Debug, thiserror::Error)]
pub enum RenderError {
#[error("Glyph not found for '{0}'")]
GlyphNotFound(char),
#[error(transparent)]
GlyphLoadingError(#[from] GlyphLoadingError),
#[error("out of bounds at {0} {1}")]
OutOfBounds(usize, usize),
}
impl FontRenderer8x8 {
pub fn new(font: Font, fallback_char: Option<char>) -> Self {
let canvas =
Canvas::new(vec2i(TILE_SIZE as i32, TILE_SIZE as i32), Format::A8);
assert_eq!(canvas.pixels.len(), TILE_SIZE * TILE_SIZE);
assert_eq!(canvas.stride, TILE_SIZE);
let fallback_char = fallback_char.and_then(|c| font.glyph_for_char(c));
let result = Self {
font,
fallback_char,
canvas: Mutex::new(canvas),
};
result
}
pub fn render(
&self,
char: char,
bitmap: &mut Bitmap,
offset: Origin<Pixels>,
) -> Result<(), RenderError> {
let mut canvas = self.canvas.lock().unwrap();
let glyph_id = self.font.glyph_for_char(char).or(self.fallback_char);
let glyph_id = match glyph_id {
None => return Err(GlyphNotFound(char)),
Some(val) => val,
};
canvas.pixels.fill(0);
self.font.rasterize_glyph(
&mut canvas,
glyph_id,
TILE_SIZE as f32,
Transform2F::from_translation(vec2f(0f32, TILE_SIZE as f32))
* Transform2F::default(),
HintingOptions::None,
RasterizationOptions::Bilevel,
)?;
for y in 0..TILE_SIZE {
for x in 0..TILE_SIZE {
let index = x + y * TILE_SIZE;
let canvas_val = canvas.pixels[index] != 0;
let bitmap_x = (offset.x + x) as isize;
let bitmap_y = (offset.y + y) as isize;
if !bitmap.set_optional(bitmap_x, bitmap_y, canvas_val) {
return Err(OutOfBounds(x, y));
}
}
}
Ok(())
}
}
impl Default for FontRenderer8x8 {
fn default() -> Self {
let utf8_font = SystemSource::new()
.select_best_match(&[FamilyName::Monospace], &Properties::new())
.unwrap()
.load()
.unwrap();
FontRenderer8x8::new(utf8_font, Some('?'))
}
}