// Copyright 2016 Joe Wilm, The Alacritty Project Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //! Rasterization powered by FreeType and FontConfig use std::collections::HashMap; use freetype::{self, Library, Face}; mod list_fonts; use self::list_fonts::{Family, get_font_families}; use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey}; /// Rasterizes glyphs for a single font face. pub struct FreeTypeRasterizer { faces: HashMap>, library: Library, system_fonts: HashMap, keys: HashMap, dpi_x: u32, dpi_y: u32, dpr: f32, } #[inline] fn to_freetype_26_6(f: f32) -> isize { ((1i32 << 6) as f32 * f) as isize } impl ::Rasterize for FreeTypeRasterizer { type Err = Error; fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Result { let library = Library::init()?; Ok(FreeTypeRasterizer { system_fonts: get_font_families(), faces: HashMap::new(), keys: HashMap::new(), library: library, dpi_x: dpi_x as u32, dpi_y: dpi_y as u32, dpr: device_pixel_ratio, }) } fn metrics(&self, key: FontKey, size: Size) -> Result { let face = self.faces .get(&key) .ok_or(Error::FontNotLoaded)?; let scale_size = self.dpr as f64 * size.as_f32_pts() as f64; let em_size = face.em_size() as f64; let w = face.max_advance_width() as f64; let h = (face.ascender() - face.descender() + face.height()) as f64; let w_scale = w * scale_size / em_size; let h_scale = h * scale_size / em_size; Ok(Metrics { average_advance: w_scale, line_height: h_scale, }) } fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result { self.keys .get(&desc.to_owned()) .map(|k| Ok(*k)) .unwrap_or_else(|| { let face = self.get_face(desc)?; let key = FontKey::next(); self.faces.insert(key, face); Ok(key) }) } fn get_glyph(&mut self, glyph_key: &GlyphKey) -> Result { let face = self.faces .get(&glyph_key.font_key) .ok_or(Error::FontNotLoaded)?; let size = glyph_key.size.as_f32_pts() * self.dpr; let c = glyph_key.c; face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y)?; face.load_char(c as usize, freetype::face::TARGET_LIGHT)?; let glyph = face.glyph(); glyph.render_glyph(freetype::render_mode::RenderMode::Lcd)?; unsafe { let ft_lib = self.library.raw(); freetype::ffi::FT_Library_SetLcdFilter( ft_lib, freetype::ffi::FT_LCD_FILTER_DEFAULT ); } let bitmap = glyph.bitmap(); let buf = bitmap.buffer(); let pitch = bitmap.pitch() as usize; let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize); for i in 0..bitmap.rows() { let start = (i as usize) * pitch; let stop = start + bitmap.width() as usize; packed.extend_from_slice(&buf[start..stop]); } Ok(RasterizedGlyph { c: c, top: glyph.bitmap_top(), left: glyph.bitmap_left(), width: glyph.bitmap().width() / 3, height: glyph.bitmap().rows(), buf: packed, }) } } impl FreeTypeRasterizer { fn get_face(&mut self, desc: &FontDesc) -> Result, Error> { self.system_fonts .get(&desc.name[..]) .and_then(|font| font.variants().get(&desc.style[..])) .ok_or_else(|| Error::MissingFont(desc.to_owned())) .and_then(|variant| Ok(self.library.new_face(variant.path(), variant.index())?)) } } /// Errors occurring when using the freetype rasterizer #[derive(Debug)] pub enum Error { /// Error occurred within the FreeType library FreeType(freetype::Error), /// Couldn't find font matching description MissingFont(FontDesc), /// Requested an operation with a FontKey that isn't known to the rasterizer FontNotLoaded, } impl ::std::error::Error for Error { fn cause(&self) -> Option<&::std::error::Error> { match *self { Error::FreeType(ref err) => Some(err), _ => None, } } fn description(&self) -> &str { match *self { Error::FreeType(ref err) => err.description(), Error::MissingFont(ref _desc) => "couldn't find the requested font", Error::FontNotLoaded => "tried to operate on font that hasn't been loaded", } } } impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { Error::FreeType(ref err) => { err.fmt(f) }, Error::MissingFont(ref desc) => { write!(f, "Couldn't find a font with {}", desc) }, Error::FontNotLoaded => { f.write_str("Tried to use a font that hasn't been loaded") } } } } impl From for Error { fn from(val: freetype::Error) -> Error { Error::FreeType(val) } } unsafe impl Send for FreeTypeRasterizer {} #[cfg(test)] mod tests { use ::FontDesc; fn font_desc() -> FontDesc { FontDesc::new("Ubuntu Mono", "Regular") } }