1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::path::Path;
use std::collections::HashMap;
use graphics::character::{ CharacterCache, Character };
use graphics::types::FontSize;
use glium::Texture2d;
use glium::backend::Facade;
use glium::texture::RawImage2d;
use image::{ Rgba, ImageBuffer };
use freetype::{ self, Face };
use Texture;


fn load_character<F: Facade>(face: &Face, facade: &F, font_size: FontSize,
                             character: char)
    -> Character<Texture>
{
    face.set_pixel_sizes(0, font_size).unwrap();
    face.load_char(character as usize, freetype::face::DEFAULT).unwrap();
    let glyph = face.glyph().get_glyph().unwrap();
    let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None).unwrap();
    let bitmap = bitmap_glyph.bitmap();
    let texture =
        if bitmap.width() != 0 {
            let image = ImageBuffer::<Rgba<u8>, _>::from_raw(
                bitmap.width() as u32, bitmap.rows() as u32,
                bitmap.buffer().iter()
                    .flat_map(|&pix| vec![255, 255, 255, pix].into_iter())
                    .collect::<Vec<_>>()
                ).expect("failed to create glyph texture");
            let image_dimensions = image.dimensions();
            Texture2d::new(
                facade,
                RawImage2d::from_raw_rgba_reversed(
                    image.into_raw(), image_dimensions
                )
            )
        } else { Texture2d::empty(facade, 1, 1) };
    let glyph_size_x = glyph.advance_x();
    let glyph_size_y = glyph.advance_y();
    Character {
        offset: [
            bitmap_glyph.left() as f64,
            bitmap_glyph.top() as f64
        ],
        size: [
            (glyph_size_x >> 16) as f64,
            (glyph_size_y >> 16) as f64
        ],
        texture: Texture::new(texture.unwrap()),
    }
}

/// Caches characters for a font.
pub struct GlyphCache<F> {
    face: Face<'static>,
    data: HashMap<FontSize, HashMap<char, Character<Texture>>>,
    facade: F,
}

impl<F> GlyphCache<F> {
     /// Constructor for a GlyphCache.
    pub fn new(font: &Path, facade: F) -> Result<Self, freetype::error::Error> {
        let freetype = try!(freetype::Library::init());
        let face = try!(freetype.new_face(font, 0));
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
            facade: facade,
        })
    }
}

impl<F: Facade> CharacterCache for GlyphCache<F> {
    type Texture = Texture;

    fn character<'a>(&'a mut self, font_size: FontSize, character: char)
        -> &'a Character<Texture>
    {
        use std::collections::hash_map::Entry::{Vacant, Occupied};
        let size_cache: &'a mut HashMap<char, _> = match self.data.entry(font_size) {
            Vacant(entry) => entry.insert(HashMap::new()),
            Occupied(entry) => entry.into_mut(),
        };
        match size_cache.entry(character) {
            Vacant(entry) => entry.insert(load_character(&self.face, &self.facade, font_size, character)),
            Occupied(entry) => entry.into_mut(),
        }
    }
}