diff --git a/textium/examples/Gudea-Regular.ttf b/textium/examples/Gudea-Regular.ttf new file mode 100644 index 0000000..a15eab8 Binary files /dev/null and b/textium/examples/Gudea-Regular.ttf differ diff --git a/textium/examples/render_char.rs b/textium/examples/render_char.rs new file mode 100644 index 0000000..efd163d --- /dev/null +++ b/textium/examples/render_char.rs @@ -0,0 +1,26 @@ +extern crate textium; +extern crate rusttype; + +use rusttype::{FontCollection}; + +fn main() { + let font_data = include_bytes!("Gudea-Regular.ttf"); + let font = FontCollection::from_bytes(font_data as &[u8]) + .into_font().unwrap(); + let font = textium::Font::new(font); + + let (metadata, bitmap) = font.rasterize_char('A', 40.0).unwrap(); + + println!("{:?}", metadata); + + for line in bitmap.lines() { + for &pixel in line { + if pixel == 0 { + print!(" "); + } else { + print!("#"); + } + } + println!(); + } +} diff --git a/textium/src/lib.rs b/textium/src/lib.rs index f0d5e88..1aef7e6 100644 --- a/textium/src/lib.rs +++ b/textium/src/lib.rs @@ -4,19 +4,19 @@ extern crate rusttype; #[macro_use] extern crate glium; +mod error; mod texture; mod geometry; -mod error; +mod rasterizer; use std::iter::Iterator; use std::collections::HashMap; use glium::backend::Facade; use glium::texture::Texture2d; -use rusttype::Font; -use texture::{Texture2dData, get_nearest_pow2}; pub use error::Error; pub use geometry::Rect; +pub use rasterizer::Font; /// Information used to render a glyph in a font. #[derive(Debug)] diff --git a/textium/src/rasterizer.rs b/textium/src/rasterizer.rs new file mode 100644 index 0000000..12bb2ea --- /dev/null +++ b/textium/src/rasterizer.rs @@ -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(&self, chrs: I, scale: f32, margin: u32, width: usize, height: usize) + -> (FontAtlas, Bitmap) + where I: Iterator + { + 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)> + { + 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 = 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)) + } +}