|
|
@ -0,0 +1,83 @@ |
|
|
|
use rusttype;
|
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use ::{FontAtlas, GlyphMetadata};
|
|
|
|
use ::geometry::Rect;
|
|
|
|
use ::texture::{Buffer2d, Bitmap};
|
|
|
|
use ::packer::SkylinePacker;
|
|
|
|
|
|
|
|
pub struct Font<'a> {
|
|
|
|
font: rusttype::Font<'a>
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
impl<'a> Font<'a> {
|
|
|
|
/// Construct a Textium font from a RustType one.
|
|
|
|
pub fn new(rt_font: rusttype::Font<'a>) -> Font {
|
|
|
|
Font {
|
|
|
|
font: rt_font
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Build a font atlas for a set of characters in a font, rasterized at a given scale.
|
|
|
|
///
|
|
|
|
/// `margin` is the distance between glyphs in the atlas, in pixels.
|
|
|
|
/// `width` and `height` are the starting size of the bitmap.
|
|
|
|
///
|
|
|
|
/// The packer may enlarge the bitmap beyond its initial dimensions in order to fit all glyphs.
|
|
|
|
pub fn make_atlas<I>(&self, chrs: I, scale: f32, margin: u32, width: usize, height: usize)
|
|
|
|
-> (FontAtlas, Bitmap<u8>)
|
|
|
|
where I: Iterator<Item=char>
|
|
|
|
{
|
|
|
|
let mut atlas = FontAtlas {glyph_data: HashMap::new()};
|
|
|
|
let mut packer = SkylinePacker::new(Bitmap::new(width, height));
|
|
|
|
packer.set_margin(margin);
|
|
|
|
for chr in chrs {
|
|
|
|
if let Some((mut info, rendered)) = self.rasterize_char(chr, scale) {
|
|
|
|
let r = packer.pack_resize(&rendered);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(atlas, packer.into_buf())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rasterize_char(&self, chr: char, scale: f32)
|
|
|
|
-> Option<(GlyphMetadata, Bitmap<u8>)>
|
|
|
|
{
|
|
|
|
let glyph = match self.font.glyph(chr) {
|
|
|
|
Some(g) => g,
|
|
|
|
None => return None, // propogate missing glyphs out of the function
|
|
|
|
}.scaled(rusttype::Scale::uniform(scale));
|
|
|
|
|
|
|
|
let h_metrics = glyph.h_metrics();
|
|
|
|
let glyph = glyph.positioned(rusttype::Point {x: 0., y: 0.});
|
|
|
|
let bbox = match glyph.pixel_bounding_box() {
|
|
|
|
Some(b) => b,
|
|
|
|
None => return None, // if the bounding box is missing, we can't rasterize
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut out: Bitmap<u8> = Bitmap::new(bbox.width() as usize, bbox.height() as usize);
|
|
|
|
glyph.draw(|x, y, v| {
|
|
|
|
out.set(x as usize, y as usize, (v * 255.0) as u8);
|
|
|
|
});
|
|
|
|
|
|
|
|
println!("{:?}", bbox);
|
|
|
|
|
|
|
|
let data = GlyphMetadata {
|
|
|
|
scale: scale,
|
|
|
|
bounding_box: Rect {
|
|
|
|
x: bbox.min.x as usize,
|
|
|
|
y: bbox.min.y as usize,
|
|
|
|
w: bbox.width() as usize,
|
|
|
|
h: bbox.height() as usize,
|
|
|
|
},
|
|
|
|
|
|
|
|
advance_width: h_metrics.advance_width,
|
|
|
|
left_bearing: h_metrics.left_side_bearing,
|
|
|
|
};
|
|
|
|
|
|
|
|
Some((data, out))
|
|
|
|
}
|
|
|
|
}
|