wgpu_font_renderer/
loader.rs

1use std::{collections::HashMap, fmt, io::Read};
2use owned_ttf_parser::{AsFaceRef, GlyphId, OutlineBuilder, OwnedFace, Rect};
3use swash::{CacheKey, FontRef};
4
5use crate::atlas::{allocation::Allocation, Atlas};
6
7#[derive(Debug)]
8pub struct Glyph {
9    pub curves: Vec<f32>,
10    pub allocation: Allocation,
11    pub bbox: Rect,
12    pub descent: i16,
13    pub y_offset: i16,
14    pub left_side_bearing: i16,
15}
16
17pub struct Font {
18    data: Vec<u8>,
19    pub face: OwnedFace,
20    pub offset: u32,
21    pub key: CacheKey,
22    pub glyph_cache: HashMap<GlyphId, Glyph>,
23}
24
25type Result<T> = std::result::Result<T, LoadingError>;
26
27#[derive(Debug)]
28pub enum LoadingError {
29    FileNotFound,
30    InvalidFile,
31}
32
33impl fmt::Display for LoadingError {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        match *self {
36            LoadingError::FileNotFound =>
37                write!(f, "TTF Font file not found"),
38            LoadingError::InvalidFile =>
39                write!(f, "TTF Font file provided is invalid"),
40        }
41    }
42}
43
44impl Font {
45    pub fn from_file(
46        device: &wgpu::Device,
47        encoder: &mut wgpu::CommandEncoder,
48        queue: &wgpu::Queue,
49        path: &str,
50        index: usize,
51        cache_preset: &str,
52        atlas: &mut Atlas
53    ) -> Result<Font> {
54        // Read the font file as bytes
55        let data = std::fs::read(path).or(Err(LoadingError::FileNotFound))?;
56        // Create a temporary font reference for the font available in the file at `index`.
57        // This will do some basic validation, compute the necessary offset
58        // and generate a fresh cache key for us.
59        let font = FontRef::from_index(&data, index).ok_or(LoadingError::InvalidFile)?;
60        let (offset, key) = (font.offset, font.key);
61
62        // Generate struct that hold TTF face tables
63        let face = OwnedFace::from_vec(data.clone(), index as u32).or(Err(LoadingError::InvalidFile))?;
64
65        // Generate glyph cache for each glyph present in the font file
66        let glyph_cache = create_glyph_cache(device, encoder, queue, &face, cache_preset, atlas);
67
68        Ok(Self { data, face, offset, key, glyph_cache })
69    }
70
71    // Create the transient font reference to access swash features
72    pub fn as_ref(&self) -> FontRef {
73        FontRef {
74            data: &self.data,
75            offset: self.offset,
76            key: self.key,
77        }
78    }
79}
80
81
82fn create_glyph_cache(
83    device: &wgpu::Device,
84    encoder: &mut wgpu::CommandEncoder,
85    queue: &wgpu::Queue,
86    face: &OwnedFace,
87    cache_preset: &str,
88    atlas: &mut Atlas
89) -> HashMap<GlyphId, Glyph> {
90    let mut glyph_cache = HashMap::new();
91
92    let face = face.as_face_ref();
93
94    let ascender = face.ascender();
95
96    for code_point in cache_preset.chars() { 
97        if let Some(glyph_id) = face.glyph_index(code_point) {
98            if let Some(bbox) = face.glyph_bounding_box(glyph_id) {
99                let height = bbox.height();
100                let left_side_bearing = bbox.x_min;
101    
102                let (descent, distance_from_baseline) = if bbox.y_min <= 0 {
103                    (bbox.y_min, 0)
104                } else {
105                    (0, bbox.y_min)
106                };
107    
108                let total_height = height + descent + distance_from_baseline;
109                let y_offset = ascender - distance_from_baseline - height;
110    
111                let mut builder = BezierBuilder::new(total_height as f32);
112    
113                face.outline_glyph(glyph_id, &mut builder);
114
115                let curves_count = builder.curves.len() as u32;
116    
117                let bytes = unsafe {
118                    std::slice::from_raw_parts(builder.curves.as_ptr() as *const u8, builder.curves.len() * 4)
119                };
120
121                if let Some(allocation) = atlas.upload(curves_count, bytes, device, encoder, queue) {
122                    let glyph = Glyph {
123                        curves: builder.curves,
124                        allocation,
125                        bbox,
126                        descent: descent,
127                        y_offset: y_offset,
128                        left_side_bearing,
129                    };
130        
131                    glyph_cache.insert(glyph_id, glyph);
132                }
133            }
134        }
135    }
136
137    glyph_cache
138}
139
140struct BezierBuilder {
141    last_position: [f32; 2],
142    pub curves: Vec<f32>,
143    total_height: f32,
144}
145
146impl BezierBuilder {
147    pub fn new(total_height: f32) -> Self {
148        Self {
149            last_position: [0., 0.],
150            curves: Vec::new(),
151            total_height,
152        }
153    }
154}
155
156impl OutlineBuilder for BezierBuilder {
157    fn move_to(&mut self, x: f32, y: f32) {
158        self.last_position = [x, self.total_height - y];
159    }
160
161    fn line_to(&mut self, x: f32, y: f32) {
162        let [x0, y0] = self.last_position;
163        self.curves.extend_from_slice(&[x0, y0, x, self.total_height - y, x, self.total_height - y, 0., 0.]);
164        self.last_position = [x, self.total_height - y];
165    }
166
167    fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
168        let [x0, y0] = self.last_position;
169        self.curves.extend_from_slice(&[x0, y0, x1, self.total_height - y1, x, self.total_height - y, 0., 0.]);
170        self.last_position = [x, self.total_height - y];
171    }
172
173    fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
174        self.curves.extend_from_slice(&[x1, self.total_height - y1, x2, self.total_height - y2, x, self.total_height - y, 0., 0.]);
175        self.last_position = [x, self.total_height - y];
176    }
177
178    fn close(&mut self) {
179        
180    }
181}