use super::{
packer2d::{Packer, Rect},
TexturePosition,
};
use font_loader::system_fonts::{self, FontProperty};
use fontdue::{Font, FontSettings};
use glium::{
backend::Facade,
texture::{
MipmapsOption, RawImage2d, Texture2dDataSink, TextureCreationError, UncompressedFloatFormat,
},
uniforms::Sampler,
Texture2d,
};
use std::{borrow::Cow, collections::HashMap};
type GlyphHash = usize;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
struct Glyph {
index: u16,
scale: u16,
font: usize,
}
pub struct Glyphs {
texture: Texture2d,
packer: Packer,
fonts: HashMap<usize, Font>,
glyphs: HashMap<Glyph, (GlyphHash, TexturePosition)>,
queue: Vec<Glyph>,
}
impl Glyphs {
pub fn new<F: Facade>(facade: &F, rect: Rect) -> Result<Self, TextureCreationError> {
let texture = Texture2d::empty_with_format(
facade,
UncompressedFloatFormat::F32,
MipmapsOption::AutoGeneratedMipmaps,
rect.width,
rect.height,
)?;
let packer = Packer::new(rect);
let fonts = HashMap::new();
let glyphs = HashMap::new();
let queue = Vec::new();
Ok(Self {
texture,
packer,
fonts,
glyphs,
queue,
})
}
pub fn add_font(&mut self, font: Font) -> usize {
let id = font.file_hash();
self.fonts.insert(id, font);
id
}
pub fn add_font_bytes(&mut self, font: &[u8]) -> Result<usize, &'static str> {
Ok(self.add_font(Font::from_bytes(font, FontSettings::default())?))
}
pub fn add_font_property(&mut self, font: FontProperty) -> Result<usize, &'static str> {
self.add_font_bytes(&system_fonts::get(&font).ok_or("Font not found")?.0[..])
}
pub fn queue(&mut self, c: char, scale: u16, font: usize) {
self.queue.push(Glyph {
index: self.fonts[&font].lookup_glyph_index(c),
scale,
font,
});
}
pub fn flush(&mut self) {
let mut tmp_queue = vec![];
std::mem::swap(&mut tmp_queue, &mut self.queue);
for queued in tmp_queue.drain(..) {
if self.get_glyph(&queued).is_some() {
continue;
}
let (metrics, data) =
self.fonts[&queued.font].rasterize_indexed(queued.index, queued.scale as f32);
let rect = self
.packer
.push(Rect::new(metrics.width as u32, metrics.height as u32))
.unwrap();
let data = RawImage2d::from_raw(Cow::from(&data[..]), rect.width, rect.height);
self.texture.write(
glium::Rect {
left: rect.x,
bottom: rect.y,
width: rect.width,
height: rect.height,
},
data,
);
let dim = Rect {
width: self.texture.dimensions().0,
height: self.texture.dimensions().1,
};
self.glyphs
.insert(queued, (0, TexturePosition::new(dim, rect)));
}
}
fn get_glyph(&self, glyph: &Glyph) -> Option<TexturePosition> {
Some(self.glyphs.get(glyph)?.1)
}
pub fn get(&self, c: char, scale: u16, font: usize) -> Option<TexturePosition> {
self.get_glyph(&Glyph {
index: self.fonts[&font].lookup_glyph_index(c),
scale,
font,
})
}
pub fn get_indexed(&self, index: u16, scale: u16, font: usize) -> Option<TexturePosition> {
self.get_glyph(&Glyph { index, scale, font })
}
pub fn sampled(&self) -> Sampler<'_, Texture2d> {
self.texture.sampled()
}
pub fn font(&self, font: usize) -> Option<&'_ Font> {
self.fonts
.get(&font)
.or_else(|| self.fonts.iter().next().map(|(_, font)| font))
}
}