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
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use std::collections::HashMap;

use failure;
use graphics::{
    character::{Character, CharacterCache},
    types::{FontSize, Scalar},
};
use rusttype::{point, Error, Font, GlyphId, Rect, Scale};

use super::*;

struct CharacterDef {
    offset: [Scalar; 2],
    size: [Scalar; 2],
    texture: RenderBuffer,
}

impl CharacterDef {
    fn as_character<'a>(&'a self) -> Character<'a, RenderBuffer> {
        Character {
            offset: self.offset,
            size: self.size,
            texture: &self.texture,
        }
    }
}

/// A character cache for drawing text to a `RenderBuffer`.
pub struct BufferGlyphs<'f> {
    characters: HashMap<(char, u32), CharacterDef>,
    font: Font<'f>,
}

impl<'f> BufferGlyphs<'f> {
    /// Loads a `BufferGlyphs` from an array of font data.
    pub fn from_bytes(bytes: &'f [u8]) -> Result<BufferGlyphs<'f>, Error> {
        Ok(BufferGlyphs {
            characters: HashMap::new(),
            font: Font::from_bytes(bytes)?,
        })
    }
    /// Loads a `BufferGlyphs` from a `Font`.
    pub fn from_font(font: Font<'f>) -> BufferGlyphs<'f> {
        BufferGlyphs {
            characters: HashMap::new(),
            font,
        }
    }
}

impl<'f> CharacterCache for BufferGlyphs<'f> {
    type Texture = RenderBuffer;
    type Error = failure::Error;
    fn character<'a>(
        &'a mut self,
        font_size: FontSize,
        ch: char,
    ) -> Result<Character<'a, Self::Texture>, Self::Error> {
        Ok(self
            .characters
            .entry((ch, font_size))
            .or_insert({
                let scale = Scale::uniform(font_size as f32);
                let glyph = self.font.glyph(ch).scaled(scale);
                let glyph = if glyph.id() == GlyphId(0) && glyph.shape().is_none() {
                    self.font.glyph('\u{FFFD}').scaled(scale)
                } else {
                    glyph
                };
                let h_metrics = glyph.h_metrics();
                let bounding_box = glyph.exact_bounding_box().unwrap_or(Rect {
                    min: point(0.0, 0.0),
                    max: point(0.0, 0.0),
                });
                let glyph = glyph.positioned(point(0.0, 0.0));
                let pixel_bounding_box = glyph.pixel_bounding_box().unwrap_or(Rect {
                    min: point(0, 0),
                    max: point(0, 0),
                });
                let pixel_bb_width = pixel_bounding_box.width() + 2;
                let pixel_bb_height = pixel_bounding_box.height() + 2;

                let mut texture = RenderBuffer::new(pixel_bb_width as u32, pixel_bb_height as u32);
                glyph.draw(|x, y, v| {
                    texture.set_pixel(x, y, [1.0, 1.0, 1.0, v]);
                });
                CharacterDef {
                    offset: [
                        bounding_box.min.x as Scalar - 1.0,
                        -pixel_bounding_box.min.y as Scalar + 1.0,
                    ],
                    size: [h_metrics.advance_width as Scalar, 0 as Scalar],
                    texture,
                }
            })
            .as_character())
    }
    fn width(&mut self, size: FontSize, text: &str) -> Result<Scalar, Self::Error> {
        text.chars().fold(Ok(0.0), |sum, c| {
            if let Ok(s) = sum {
                if let Ok(ch) = self.character(size, c) {
                    Ok(s + ch.width())
                } else {
                    sum
                }
            } else {
                sum
            }
        })
    }
}