font_atlas/cache/
mod.rs

1#![deny(missing_docs)]
2
3use std::collections::HashMap;
4use super::rasterize::{Font, Bitmap, Atlas, CharInfo};
5use super::glyph_packer;
6use void::Void;
7
8/// A cache that stores multiple fonts types of multiple weights.
9///
10/// The basic usage for a FontCache is to load all of the fonts that
11/// you plan on using into the FontCache, then call `create_face` for
12/// each of the font/size combinations that you see yourself using.
13#[derive(Debug)]
14pub struct FontCache<T> {
15    base_font: HashMap<String, Font>,
16    cached: Vec<(String, f32, FaceCache<T>)>,
17}
18
19/// A cache for a single font/size pairing.
20#[derive(Debug)]
21pub struct FaceCache<T> {
22    font: Font,
23    bitmap: T,
24    scale: f32,
25    atlas: Atlas,
26    missing: HashMap<char, Option<T>>,
27    missing_info: HashMap<char, CharInfo>,
28    line_height: f32,
29}
30
31/// A command instructing the user on how to draw a
32/// single character.
33#[derive(Debug)]
34pub struct DrawCommand<'a, T: 'a> {
35    /// The bitmap that contains the character
36    pub bitmap: &'a T,
37    /// The location of the character in the bitmap
38    pub bitmap_location: glyph_packer::Rect,
39    /// The location on the screen to draw the character
40    pub draw_location: (f32, f32),
41}
42
43/// Drawing text is no easy business.  Here's a list of
44/// everything that could go wrong.
45#[derive(Debug)]
46pub enum FontCacheError<E> {
47    /// An error that comes from the bitmap transformation
48    UserError(E),
49    /// The font has not been loaded
50    NoLoadedFont(String),
51    /// A face has not been loaded and then rendered
52    NoRenderedFace(String, f32),
53    /// A glyph is missing from the font file
54    MissingGlyph(char),
55}
56
57impl <T> FontCache<T> {
58    /// Create an empty FontCache.
59    pub fn new() -> FontCache<T> {
60        FontCache {
61            base_font: HashMap::new(),
62            cached: Vec::new(),
63        }
64    }
65
66    /// Loads a font into the cache with a given name.  This will allow you to call
67    /// `create_face` passing in the name in order to generate a rendering of this
68    /// font at a given size.
69    pub fn load_font<S: Into<String>>(&mut self, name: S, font: Font) {
70        self.base_font.insert(name.into(), font);
71    }
72
73    /// Given the name of an already-loaded font and the scale at which to draw it,
74    /// create_face generates an atlas with the characters in `chars` and stores it
75    /// this cache.
76    ///
77    /// The function `f` is used to transform a character bitmap into whatever
78    /// format you want to use internally.
79    pub fn create_face<I, F, E>(&mut self, name: &str, scale: f32, chars: I, f: F) -> Result<(), FontCacheError<E>>
80    where I: Iterator<Item=char>, F: Fn(Bitmap) -> Result<T, E> {
81        if self.cached.iter().any(|&(ref n, s, _)| n == name && scale == s) {
82            return Ok(());
83        }
84
85        match self.base_font.get(name).cloned() {
86            Some(font) => {
87                let fc = try!(FaceCache::new(font, scale, chars, f).or_else(|e| Err(FontCacheError::UserError(e))));
88                self.cached.push((name.into(), scale, fc));
89                return Ok(());
90            }
91            None => return Err(FontCacheError::NoLoadedFont(name.into()))
92        };
93    }
94
95    /// Retrieves a facecache reference given the name of the font and a scale (assuming one exists).
96    pub fn get_face_cache(&self, name: &str, scale: f32) -> Option<&FaceCache<T>> {
97        self.cached.iter()
98                   .filter(|&&(ref n, s, _)| n == name && scale == s)
99                   .map(|&(_, _, ref fc)| fc)
100                   .next()
101    }
102
103    /// Retrieves a mutable facecache given the name of the font and a scale (assuming one exists).
104    pub fn get_face_cache_mut(&mut self, name: &str, scale: f32) -> Option<&mut FaceCache<T>> {
105        self.cached.iter_mut()
106                   .filter(|&&mut (ref n, s, _)| n == name && scale == s)
107                   .map(|&mut (_, _, ref mut fc)| fc)
108                   .next()
109    }
110
111    /// Returns the drawing commands for a given font name, scale and the string to draw.
112    ///
113    /// Can fail if the font hasn't been rasterized with `create_face`.
114    pub fn drawing_commands(&self, font_name: &str, scale: f32, string: &str) -> Result<Vec<DrawCommand<T>>, FontCacheError<Void>> {
115        let fc = try!(self.get_face_cache(font_name, scale).ok_or(FontCacheError::NoRenderedFace(font_name.into(), scale)));
116        fc.drawing_commands(string)
117    }
118
119    /// Returns the drawing commands for a given font name, scale, and the string to draw.
120    ///
121    /// If the font hasn't been loaded with `load_font` an error is returned.
122    ///
123    /// If the font hasn't been rasterized with `create_face`, it is done so now.
124    pub fn drawing_commands_prepared<F, E>(&mut self, font_name: &str, scale: f32, string: &str, f: F) -> Result<Vec<DrawCommand<T>>, FontCacheError<E>>
125    where F: Fn(Bitmap) -> Result<T, E> {
126        if self.get_face_cache(font_name, scale).is_none() {
127            try!(self.create_face(font_name, scale, string.chars(), |a| f(a)));
128        }
129        {
130            let fc = self.get_face_cache_mut(font_name, scale).unwrap();
131            try!(fc.prepare_string(string, f).map_err(FontCacheError::UserError));
132        }
133        Ok(self.drawing_commands(font_name, scale, string).unwrap())
134    }
135}
136
137impl <T> FaceCache<T> {
138    /// Constructs a new FaceCache of a font at a specific scale.
139    ///
140    /// The characters in `chars` are pre-loaded into the atlas.
141    /// If you need any more characters to draw things with, use
142    /// `prepare_string`.
143    pub fn new<I, F, E>(font: Font, scale: f32, chars: I, f: F) -> Result<FaceCache<T>, E>
144    where I: Iterator<Item=char>, F: Fn(Bitmap) -> Result<T, E>
145    {
146            let (atlas, bitmap, line_height) = font.make_atlas(chars, scale, 3, 256, 256);
147            let bitmap = try!(f(bitmap));
148            Ok(FaceCache {
149                font: font,
150                atlas: atlas,
151                bitmap: bitmap,
152                scale: scale,
153                missing: HashMap::new(),
154                missing_info: HashMap::new(),
155                line_height: line_height,
156            })
157    }
158
159    /// Adds all the characters in the given string to the cache.
160    pub fn prepare_string<F, E>(&mut self, s: &str, f: F) -> Result<(), E>
161    where F: Fn(Bitmap) -> Result<T, E>
162    {
163        for c in s.chars() {
164            if self.atlas.info(c).is_none() && !self.missing.contains_key(&c) {
165                match self.font.render_char(c, self.scale).map(|(i, a)| (i, f(a))) {
166                    Some((i, Ok(b))) => {
167                        self.missing.insert(c, Some(b));
168                        self.missing_info.insert(c, i);
169                    },
170                    Some((_, Err(e))) => return Err(e),
171                    None => {
172                        self.missing.insert(c, None);
173                    },
174                };
175            }
176        }
177        Ok(())
178    }
179
180    /// Returns true if a call to `prepare_string` is necessary to
181    /// draw this string.
182    pub fn needs_preparing(&self, s: &str) -> bool {
183        for c in s.chars() {
184            if self.atlas.info(c).is_none() && !self.missing.contains_key(&c) {
185                return true;
186            }
187        }
188        return false;
189    }
190
191    /// Returns a vector of drawing commands that describe how to lay out
192    /// characters for printing
193    pub fn drawing_commands(&self, s: &str) -> Result<Vec<DrawCommand<T>>, FontCacheError<Void>> {
194        let mut out = Vec::new();
195        let mut x = 0.0;
196        let mut y = self.line_height.floor();
197
198        for c in s.chars() {
199            if c == ' ' {
200                if let Some(ci) = self.atlas.info('w').or_else(|| self.missing_info.get(&c).cloned()) {
201                    x += ci.bounding_box.w as f32;
202                    continue;
203                } 
204            }
205
206            let bitmap;
207            let info;
208
209            if let Some(ci) = self.atlas.info(c) {
210                bitmap = &self.bitmap;
211                info = ci;
212            } else if let Some(ci) = self.missing_info.get(&c).cloned() {
213                bitmap = self.missing.get(&c).unwrap().as_ref().unwrap();
214                info = ci;
215            } else {
216                return Err(FontCacheError::MissingGlyph(c));
217            }
218
219            x += info.pre_draw_advance.0;
220            y += info.pre_draw_advance.1;
221
222            let h = info.bounding_box.h as f32;
223
224            out.push(DrawCommand {
225                bitmap: bitmap,
226                bitmap_location: info.bounding_box,
227                draw_location: (x, y - h),
228            });
229
230            x += info.bounding_box.w as f32;
231        }
232        Ok(out)
233    }
234
235    /// Returns a Vec of DrawCommands that pre-prepare the face-cache for drawing.
236    ///
237    /// The only way that this will fail is if the rendering function `f` fails.
238    pub fn drawing_commands_prepared<F, E>(&mut self, s: &str, f: F) -> Result<Vec<DrawCommand<T>>, E>
239    where F: Fn(Bitmap) -> Result<T, E> {
240        try!(self.prepare_string(s, f));
241        Ok(self.drawing_commands(s).unwrap())
242    }
243}