use std::{collections::HashMap, fmt, io::Read};
use owned_ttf_parser::{AsFaceRef, GlyphId, OutlineBuilder, OwnedFace, Rect};
use swash::{CacheKey, FontRef};
use crate::atlas::{allocation::Allocation, Atlas};
#[derive(Debug)]
pub struct Glyph {
pub curves: Vec<f32>,
pub allocation: Allocation,
pub bbox: Rect,
pub descent: i16,
pub y_offset: i16,
pub left_side_bearing: i16,
}
pub struct Font {
data: Vec<u8>,
pub face: OwnedFace,
pub offset: u32,
pub key: CacheKey,
pub glyph_cache: HashMap<GlyphId, Glyph>,
}
type Result<T> = std::result::Result<T, LoadingError>;
#[derive(Debug)]
pub enum LoadingError {
FileNotFound,
InvalidFile,
}
impl fmt::Display for LoadingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LoadingError::FileNotFound =>
write!(f, "TTF Font file not found"),
LoadingError::InvalidFile =>
write!(f, "TTF Font file provided is invalid"),
}
}
}
impl Font {
pub fn from_file(
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
queue: &wgpu::Queue,
path: &str,
index: usize,
cache_preset: &str,
atlas: &mut Atlas
) -> Result<Font> {
let data = std::fs::read(path).or(Err(LoadingError::FileNotFound))?;
let font = FontRef::from_index(&data, index).ok_or(LoadingError::InvalidFile)?;
let (offset, key) = (font.offset, font.key);
let face = OwnedFace::from_vec(data.clone(), index as u32).or(Err(LoadingError::InvalidFile))?;
let glyph_cache = create_glyph_cache(device, encoder, queue, &face, cache_preset, atlas);
Ok(Self { data, face, offset, key, glyph_cache })
}
pub fn as_ref(&self) -> FontRef {
FontRef {
data: &self.data,
offset: self.offset,
key: self.key,
}
}
}
fn create_glyph_cache(
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
queue: &wgpu::Queue,
face: &OwnedFace,
cache_preset: &str,
atlas: &mut Atlas
) -> HashMap<GlyphId, Glyph> {
let mut glyph_cache = HashMap::new();
let face = face.as_face_ref();
let ascender = face.ascender();
for code_point in cache_preset.chars() {
if let Some(glyph_id) = face.glyph_index(code_point) {
if let Some(bbox) = face.glyph_bounding_box(glyph_id) {
let height = bbox.height();
let left_side_bearing = bbox.x_min;
let (descent, distance_from_baseline) = if bbox.y_min <= 0 {
(bbox.y_min, 0)
} else {
(0, bbox.y_min)
};
let total_height = height + descent + distance_from_baseline;
let y_offset = ascender - distance_from_baseline - height;
let mut builder = BezierBuilder::new(total_height as f32);
face.outline_glyph(glyph_id, &mut builder);
let curves_count = builder.curves.len() as u32;
let bytes = unsafe {
std::slice::from_raw_parts(builder.curves.as_ptr() as *const u8, builder.curves.len() * 4)
};
if let Some(allocation) = atlas.upload(curves_count, bytes, device, encoder, queue) {
let glyph = Glyph {
curves: builder.curves,
allocation,
bbox,
descent: descent,
y_offset: y_offset,
left_side_bearing,
};
glyph_cache.insert(glyph_id, glyph);
}
}
}
}
glyph_cache
}
struct BezierBuilder {
last_position: [f32; 2],
pub curves: Vec<f32>,
total_height: f32,
}
impl BezierBuilder {
pub fn new(total_height: f32) -> Self {
Self {
last_position: [0., 0.],
curves: Vec::new(),
total_height,
}
}
}
impl OutlineBuilder for BezierBuilder {
fn move_to(&mut self, x: f32, y: f32) {
self.last_position = [x, self.total_height - y];
}
fn line_to(&mut self, x: f32, y: f32) {
let [x0, y0] = self.last_position;
self.curves.extend_from_slice(&[x0, y0, x, self.total_height - y, x, self.total_height - y, 0., 0.]);
self.last_position = [x, self.total_height - y];
}
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
let [x0, y0] = self.last_position;
self.curves.extend_from_slice(&[x0, y0, x1, self.total_height - y1, x, self.total_height - y, 0., 0.]);
self.last_position = [x, self.total_height - y];
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
self.curves.extend_from_slice(&[x1, self.total_height - y1, x2, self.total_height - y2, x, self.total_height - y, 0., 0.]);
self.last_position = [x, self.total_height - y];
}
fn close(&mut self) {
}
}