-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace freetype with rusttype #288
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,22 @@ | ||
//! Glyph caching | ||
|
||
extern crate freetype as ft; | ||
extern crate rusttype as rt; | ||
|
||
use std::path::Path; | ||
use std::io; | ||
use std::path::{Path}; | ||
use std::collections::hash_map::{ HashMap, Entry }; | ||
use graphics::character::{ CharacterCache, Character }; | ||
use graphics::types::{FontSize, Scalar}; | ||
use self::ft::render_mode::RenderMode; | ||
use { gfx, Texture, TextureSettings }; | ||
use gfx::core::factory::CombinedError; | ||
|
||
/// An enum to represent various possible run-time errors that may occur. | ||
#[derive(Clone, Debug, PartialEq)] | ||
#[derive(Debug)] | ||
pub enum Error { | ||
/// An error happened when creating a gfx texture. | ||
Texture(CombinedError), | ||
/// An error happened with the FreeType library. | ||
Freetype(ft::error::Error) | ||
/// An io error happened when reading font files. | ||
IoError(io::Error), | ||
} | ||
|
||
impl From<CombinedError> for Error { | ||
|
@@ -25,30 +25,38 @@ impl From<CombinedError> for Error { | |
} | ||
} | ||
|
||
impl From<ft::error::Error> for Error { | ||
fn from(ft_err: ft::error::Error) -> Self { | ||
Error::Freetype(ft_err) | ||
impl From<io::Error> for Error { | ||
fn from(io_error: io::Error) -> Self { | ||
Error::IoError(io_error) | ||
} | ||
} | ||
|
||
/// A struct used for caching rendered font. | ||
/// A struct used for caching a rendered font. | ||
pub struct GlyphCache<R, F> where R: gfx::Resources { | ||
/// The font face. | ||
pub face: ft::Face<'static>, | ||
/// The font. | ||
pub font: rt::Font<'static>, | ||
factory: F, | ||
// Maps from fontsize and character to offset, size and texture. | ||
data: HashMap<(FontSize, char), ([Scalar; 2], [Scalar; 2], Texture<R>)> | ||
} | ||
|
||
impl<R, F> GlyphCache<R, F> where R: gfx::Resources { | ||
/// Constructor for a GlyphCache. | ||
pub fn new<P>(font: P, factory: F) -> Result<Self, Error> | ||
where P: AsRef<Path> | ||
{ | ||
let freetype = try!(ft::Library::init()); | ||
let face = try!(freetype.new_face(font.as_ref(), 0)); | ||
pub fn new<P>(font_path: P, factory: F) -> Result<Self, Error> | ||
where P: AsRef<Path> { | ||
|
||
use std::io::Read; | ||
use std::fs::File; | ||
|
||
let mut file = try!(File::open(font_path)); | ||
let mut file_buffer = Vec::new(); | ||
try!(file.read_to_end(&mut file_buffer)); | ||
|
||
let collection = rt::FontCollection::from_bytes(file_buffer); | ||
let font = collection.into_font().unwrap(); // only succeeds if collection consists of one font | ||
|
||
Ok(GlyphCache { | ||
face: face, | ||
font: font, | ||
factory: factory, | ||
data: HashMap::new(), | ||
}) | ||
|
@@ -66,6 +74,8 @@ impl<R, F> CharacterCache for GlyphCache<R, F> where | |
size: FontSize, | ||
ch: char | ||
) -> Character<'a, Self::Texture> { | ||
let size = ((size as f32) * 1.333).round() as u32 ; // convert points to pixels | ||
|
||
match self.data.entry((size, ch)) { | ||
//returning `into_mut()' to get reference with 'a lifetime | ||
Entry::Occupied(v) => { | ||
|
@@ -77,31 +87,43 @@ impl<R, F> CharacterCache for GlyphCache<R, F> where | |
} | ||
} | ||
Entry::Vacant(v) => { | ||
self.face.set_pixel_sizes(0, size).unwrap(); | ||
self.face.load_char(ch as usize, ft::face::DEFAULT).unwrap(); | ||
let glyph = self.face.glyph().get_glyph().unwrap(); | ||
let bitmap_glyph = glyph.to_bitmap(RenderMode::Normal, None).unwrap(); | ||
let glyph_size = [glyph.advance_x(), glyph.advance_y()]; | ||
// fallback to glyph zero or U+FFFD if glyph is not present | ||
let glyph = self.font.glyph(ch).unwrap_or(self.font.glyph(rt::Codepoint(0)).unwrap_or(self.font.glyph('\u{FFFD}').unwrap())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a small tidbit - I think this line could be refactored to use let glyph = self.font.glyph(ch)
.or_else(|| self.font.glyph(rt::Codepoint(0)))
.or_else(|| self.font.glyph('\u{FFFD}'))
.unwrap(); Using Similarly, lines 95 and 97 could probably use Also, we should probably return some sort of |
||
|
||
let glyph = glyph.scaled(rt::Scale::uniform(size as f32)); | ||
let h_metrics = glyph.h_metrics(); | ||
let bounding_box = glyph.exact_bounding_box().unwrap_or(rt::Rect{min: rt::Point{x: 0.0, y: 0.0}, max: rt::Point{x: 0.0, y: 0.0} }); | ||
let glyph = glyph.positioned(rt::point(0.0, 0.0)); | ||
let pixel_bounding_box = glyph.pixel_bounding_box().unwrap_or(rt::Rect{min: rt::Point{x: 0, y: 0}, max: rt::Point{x: 0, y: 0} }); | ||
let pixel_bb_width = pixel_bounding_box.width(); | ||
let pixel_bb_height = pixel_bounding_box.height(); | ||
|
||
let mut image_buffer = Vec::<u8>::new(); | ||
image_buffer.resize((pixel_bb_width * pixel_bb_height) as usize, 0); | ||
glyph.draw(|x, y, v| { | ||
let pos = (x + y * (pixel_bb_width as u32)) as usize; | ||
image_buffer[pos] = (255.0 * v) as u8; | ||
}); | ||
|
||
let &mut (offset, size, ref texture) = v.insert(( | ||
[ | ||
bitmap_glyph.left() as f64, | ||
bitmap_glyph.top() as f64 | ||
bounding_box.min.x as Scalar, | ||
-pixel_bounding_box.min.y as Scalar, | ||
], | ||
[ | ||
(glyph_size[0] >> 16) as f64, | ||
(glyph_size[1] >> 16) as f64 | ||
h_metrics.advance_width as Scalar, | ||
0 as Scalar, | ||
], | ||
{ | ||
let bitmap = bitmap_glyph.bitmap(); | ||
if bitmap.width() == 0 || bitmap.rows() == 0 { | ||
if pixel_bb_width == 0 || pixel_bb_height == 0 { | ||
Texture::empty(&mut self.factory) | ||
.unwrap() | ||
} else { | ||
Texture::from_memory_alpha( | ||
&mut self.factory, | ||
bitmap.buffer(), | ||
bitmap.width() as u32, | ||
bitmap.rows() as u32, | ||
&image_buffer, | ||
pixel_bb_width as u32, | ||
pixel_bb_height as u32, | ||
&TextureSettings::new() | ||
).unwrap() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't quite right. As mentioned in the documentation for
glyph
, it will handle returning glyph zero (which would bert::GlyphID(0)
, notrt::Codepoint(0)
) for you, as defined by the TrueType standard. The thing you need to check is whether, in the case that glyph zero is returned, that glyph has no shape, in which case you should tryrt::Codepoint('\u{FFFD}')
. You can check the returned glyph ID with theid
method. To check whether there is a shape for the glyph, unfortunately I don't have a direct method for that, but you can query the shape itself or the bounding box on aScaledGlyph
, which will both returnNone
if there is no shape.