use crate::color::Color;
use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use fontconfig::Fontconfig;
use fontdue::{layout::GlyphRasterConfig, FontSettings, Metrics};
use image::{Pixel, RgbaImage};
pub struct Font {
font: fontdue::Font,
scale: f32,
glyph_cache: HashMap<GlyphRasterConfig, (Metrics, Vec<u8>)>,
}
impl Font {
pub fn new(name: &str, size: f32) -> Font {
let fc = Fontconfig::new().expect("Couldn't load fontconfig");
let font_path = fc.find(name, None).expect("Couldn't find font").path;
let mut font_file = File::open(font_path.to_str().unwrap()).expect("Couldn't load font file");
let mut font_buffer = Vec::new();
font_file.read_to_end(&mut font_buffer).expect("Couldn't load font file");
Font {
font: fontdue::Font::from_bytes(font_buffer, FontSettings::default())
.expect("Failed to parse Font"),
scale: size,
glyph_cache: HashMap::new(),
}
}
fn render_glyph(&mut self, conf: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
if let Some(bitmap) = self.glyph_cache.get(&conf) {
bitmap.clone()
} else {
self.glyph_cache
.insert(conf, self.font.rasterize_config(conf));
self.glyph_cache.get(&conf).unwrap().clone()
}
}
pub fn render(
&mut self,
text: &str,
color: &Color,
image: &mut RgbaImage,
x_offset: u32,
y_offset: u32,
) -> (u32, u32) {
let mut width = 0.;
for letter in text.chars() {
let (meta, bitmap) = self.render_glyph(GlyphRasterConfig {
glyph_index: self.font.lookup_glyph_index(letter) as u16,
px: self.scale,
font_index: 0,
});
for (i, alpha) in bitmap.iter().enumerate() {
if alpha != &0 {
let x = ((i % meta.width) as f32 + width + x_offset as f32 + meta.xmin as f32)
as u32;
let y = ((i as f32 / meta.width as f32) + y_offset as f32 + self.scale
- meta.height as f32
- meta.ymin as f32) as u32;
if x < image.width() && y < image.height() {
if alpha == &255 {
image.put_pixel(x, y, image::Rgba([color.0, color.1, color.2, 255]));
} else {
image
.get_pixel_mut(x, y)
.blend(&image::Rgba([color.0, color.1, color.2, *alpha]));
}
}
}
}
width += meta.advance_width;
}
(width as u32, self.scale as u32)
}
}