gust_render/
font.rs

1//
2//  Rust file | 2018
3//  Author: Alexandre Fourcat
4//  font.rs
5//  module:
6//! font datastructures and fonctions made from Font.cpp of SFML
7
8use self::ft::{
9    bitmap::PixelMode,
10    face::{Face, LoadFlag},
11    library::Library,
12};
13use super::Vector;
14use rect::Rect;
15use std::{collections::HashMap, error::Error, fmt};
16use texture::{RgbMode, Texture};
17
18extern crate freetype as ft;
19
20static SIZE: u32 = 10;
21
22/// Map defining a font <size -> Glyphmap>
23type FontMap = HashMap<u32, GlyphMap>;
24
25/// Map for each character <code -> Graphical Informations>
26type Utf8Map = HashMap<u32, CharInfo>;
27
28#[derive(Debug)]
29/// # GlyphMap
30/// A glyphmap represent a font for the size x.
31/// ## Texture
32/// the texture contain all needed character from
33/// all text which this Font is used.
34/// ## Rows
35/// The rows 'slice' the texture into each character and help to find place
36/// for each new glyph.
37/// ## Utf8Map
38/// the Utf8Map store information about
39/// each previously added Char to texture(Graphical offsets and textureCoords).
40struct GlyphMap {
41    pub texture: Texture,
42    pub rows: Vec<Row>,
43    pub map: Utf8Map,
44}
45
46#[derive(Debug, Clone)]
47struct Row {
48    pub width: u32,
49    pub height: u32,
50    pub pos: u32,
51}
52
53impl Row {
54    pub fn new(height: u32, pos: u32) -> Row {
55        Row {
56            width: 0,
57            height,
58            pos,
59        }
60    }
61}
62
63/// Contain the global texture and texture information
64impl GlyphMap {
65    /// Create a new glyph_map
66    pub fn new() -> GlyphMap {
67        let mut data: Vec<u8> = vec![255; 128 * 128 * 4];
68        for elem in data.chunks_mut(4) {
69            elem[3] = 0
70        }
71
72        GlyphMap {
73            texture: Texture::from_slice(data.as_mut_slice(), RgbMode::RGBA, 128, 128),
74            rows: Vec::with_capacity(1),
75            map: Utf8Map::with_capacity(10),
76        }
77    }
78
79    /// Get texture rect from width and height of a char.
80    /// And return information about newly inserted char.
81    /// Heavy function.
82    pub fn get_texture_rect(&mut self, width: u32, height: u32) -> Rect<u32> {
83        let mut ret: Option<Rect<u32>> = None;
84        // Iter over all element
85        for mut row in self.rows.iter_mut() {
86            if (row.width + width > self.texture.width()) || (row.height < height) {
87                continue;
88            }
89            ret = Some(Rect::new(row.width, row.pos, width, height));
90            row.width += width + 1;
91        }
92
93        if let Some(rect) = ret {
94            // Return the rect
95            rect
96        } else {
97            // iter on row to have the most y
98            let last_pos = self.rows.iter().map(|x| x.height).sum();
99
100            // while last_pos
101            while last_pos + (height + height / 10) > self.texture.height()
102                || width > self.texture.width()
103            {
104                let mut new = Texture::from_size(Vector::new(
105                    self.texture.width() * 2,
106                    self.texture.height() * 2,
107                ));
108                new.update_from_texture(&self.texture, Vector::new(0, 0))
109                    .unwrap();
110                self.texture = new;
111            }
112            let mut new_row = Row::new(height + height / 10, last_pos);
113            let new_ret = Rect::new(new_row.width, last_pos, width, height + height / 10);
114            new_row.width += width + 1;
115            self.rows.push(new_row);
116            new_ret
117        }
118    }
119
120    /// Create a new texture from Utf8Map
121    pub fn update_texture(&mut self, char_info: &CharInfo, data: &[u8]) -> Result<(), Box<Error>> {
122        self.texture.update_block(
123            data,
124            Vector::new(char_info.tex_coord.width, char_info.tex_coord.height),
125            Vector::new(char_info.tex_coord.left, char_info.tex_coord.top),
126            RgbMode::RGBA,
127        )?;
128        Ok(())
129    }
130}
131
132#[derive(Debug, Default)]
133/// # CharInfo
134/// CharInfo are data struct used into Utf8Map.
135/// ## Rect
136/// Is the offsets of the glyph like bearing etc...
137/// ## tex_coord
138/// Is the TexCoord of the char inside the GlyphMap.
139/// ## advance
140/// Is the global x offset between the previous char and the next one.
141pub struct CharInfo {
142    pub rect: Rect<f32>,
143    pub tex_coord: Rect<u32>,
144    pub advance: f32,
145}
146
147impl CharInfo {
148    /// Create an empty CharInfo
149    pub fn new() -> CharInfo {
150        CharInfo {
151            rect: Default::default(),
152            tex_coord: Default::default(),
153            advance: 0.0,
154        }
155    }
156
157    /// Create CharInfo from data
158    pub fn from_data(rect: Rect<f32>, tex_coord: Rect<u32>, advance: f32) -> CharInfo {
159        CharInfo {
160            rect,
161            tex_coord,
162            advance,
163        }
164    }
165}
166
167/// Contain a face and everything needed to render a glyph.
168/// The font have to be modified by the Text that old it
169/// so most of the time you will need to wrap it into MutResource<Self>.
170pub struct Font {
171    face: Face,
172    lib: Library,
173    map: FontMap,
174}
175
176impl fmt::Display for Font {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        write!(f, "Face: {:?}", self.face)
179    }
180}
181
182impl fmt::Debug for Font {
183    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184        write!(f, "Face: {:?}", self.face)
185    }
186}
187
188impl Font {
189    /// Create a new font from a file path.
190    pub fn from_path(path: &str) -> Option<Font> {
191        let lib = Library::init().unwrap();
192        match lib.new_face(path, 0) {
193            Err(err) => {
194                println!("Font loading error: {}", err);
195                None
196            }
197            Ok(face) => {
198                face.set_pixel_sizes(0, 30).unwrap();
199                Some(Font {
200                    face,
201                    lib,
202                    map: FontMap::with_capacity(1),
203                })
204            }
205        }
206    }
207
208    /// Check if a glyph exist.
209    pub fn glyph_exist(&mut self, size: u32, code: u32) -> bool {
210        if let Some(ref mut map_size) = self.map.get(&size) {
211            if let Some(ref mut _char_info) = map_size.map.get(&code) {
212                return true;
213            }
214        }
215        false
216    }
217
218    /// Get mutable FontMap.
219    fn get_map_mut(&mut self) -> &mut FontMap {
220        &mut self.map
221    }
222
223    /// Create a glyph if the previously asked isn't already created.
224    /// Heavy fonction.
225    fn create_glyph<'a>(&'a mut self, size: u32, code: u32) -> Result<&'a CharInfo, Box<Error>> {
226        {
227            // Get the glyph map
228            let glyph_map = self.map.entry(size).or_insert_with(GlyphMap::new);
229
230            // Load the right glyph
231            self.face.load_char(code as usize, LoadFlag::RENDER)?;
232
233            let metrics = self.face.glyph().metrics();
234            let bitmap = self.face.glyph().bitmap();
235            // Create the new Charinfo that will be inserted
236
237            let height = bitmap.rows();
238            let width = bitmap.width();
239
240            // Get the glyph and informations
241            let mut to_insert = CharInfo::new();
242            to_insert.rect.left = metrics.horiBearingX as f32 / (1 << 6) as f32;
243            to_insert.rect.top = -metrics.horiBearingY as f32 / (1 << 6) as f32;
244            to_insert.rect.width = metrics.width as f32 / (1 << 6) as f32;
245            to_insert.rect.height = metrics.height as f32 / (1 << 6) as f32;
246            to_insert.advance = (metrics.horiAdvance + 2) as f32 / (1 << 6) as f32;
247
248            // Look at the glyph texture and try to find a place inside it
249            to_insert.tex_coord = glyph_map.get_texture_rect(width as u32, height as u32);
250
251            // Resize buffer
252            let mut data = vec![255; (height * width * 4) as usize];
253            for elem in &mut data.chunks_mut(4) {
254                elem[3] = 0;
255            }
256
257            // fill pixel buffer
258            let pixels: Vec<u8> = Vec::from(bitmap.buffer());
259            let mut offset = 0;
260            match bitmap.pixel_mode().unwrap() {
261                PixelMode::None => {
262                    panic!("Error while creating glyph");
263                }
264                PixelMode::Mono => {
265                    // If it's mono just change the alpha of each pixel to make it black or white
266                    for y in 0..height {
267                        for x in 0..width {
268                            let index = ((x + y * width) * 4 + 3) as usize;
269                            let pix = pixels[(offset + (x / 8)) as usize];
270
271                            data[index] = if (pix & (1 << (7 - (x % 8)))) != 0 {
272                                255
273                            } else {
274                                0
275                            };
276                        }
277                        offset += bitmap.pitch();
278                    }
279                }
280                _ => {
281                    // Just change the alpha to make thw whole blakc or white
282                    for y in 0..height {
283                        for x in 0..width {
284                            let index = ((x + y * width) * 4 + 3) as usize;
285                            let pix = pixels[(offset + x) as usize];
286
287                            data[index] = pix;
288                        }
289                        offset += bitmap.pitch();
290                    }
291                }
292            }
293
294            // Update the texture at the right position
295            glyph_map.update_texture(&to_insert, data.as_slice())?;
296
297            // Insert the new glyph map into the hasmap
298            glyph_map.map.insert(code, to_insert);
299        }
300
301        // Return the newly inserted charinfo
302        Ok(&self.get_map_mut()[&size].map[&code])
303    }
304
305    /// Check if the glyph exist:
306    /// If the glyph exist get GraphicChar from it
307    /// Else add it to the row and update the texture
308    pub fn glyph(&mut self, size: u32, code: u32) -> &CharInfo {
309        let glyph_exist: bool = self.glyph_exist(size, code);
310
311        if glyph_exist {
312            &self.map[&size].map[&code]
313        } else {
314            self.create_glyph(size, code).unwrap()
315        }
316    }
317
318    pub fn texture(&self, font_size: u32) -> Result<&Texture, TextError> {
319        if let Some(t) = &self.map.get(&font_size) {
320            Ok(&t.texture)
321        } else {
322            Err(TextError::NoTexture)
323        }
324    }
325}
326
327#[derive(Debug)]
328pub enum TextError {
329    NoTexture,
330}
331
332impl fmt::Display for TextError {
333    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334        match self {
335            TextError::NoTexture => write!(f, "No texture try updating before."),
336        }
337    }
338}
339
340impl Error for TextError {
341    fn cause(&self) -> Option<&Error> {
342        match self {
343            TextError::NoTexture => None,
344        }
345    }
346}