diff options
author | Joe Wilm <joe@jwilm.com> | 2016-08-12 13:38:41 -0500 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-08-12 19:30:14 -0500 |
commit | b70394170c0fa9cd4e4be8150121a698a426a069 (patch) | |
tree | 41d84240a4e58df201fbd7f4f984c3814c7cb055 /src/renderer | |
parent | 874719580ce3e12f09d01a9a53c5e64205ef5b5d (diff) | |
download | r-alacritty-b70394170c0fa9cd4e4be8150121a698a426a069.tar.gz r-alacritty-b70394170c0fa9cd4e4be8150121a698a426a069.tar.bz2 r-alacritty-b70394170c0fa9cd4e4be8150121a698a426a069.zip |
Support bold/italic font rendering on macOS
This patch adds support for rendering italic fonts and bold fonts.
The `font` crate has a couple of new paradigms to support this: font
keys and glyph keys. `FontKey` is a lightweight (4 byte) identifier for
a font loaded out of the rasterizer. This replaces `FontDesc` for
rasterizing glyphs from a loaded font. `FontDesc` had the problem that
it contained two strings, and the glyph cache needs to store a copy of
the font key for every loaded glyph. `GlyphKey` is now passed to the
glyph rasterization method instead of a simple `char`. `GlyphKey`
contains information including font, size, and the character.
The rasterizer APIs do not define what happens when loading the same
font from a `FontDesc` more than once. It is assumed that the
application will track the resulting `FontKey` instead of asking the
font to be loaded multiple times.
Diffstat (limited to 'src/renderer')
-rw-r--r-- | src/renderer/mod.rs | 180 |
1 files changed, 147 insertions, 33 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index fec6cf85..23157456 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -22,13 +22,14 @@ use std::sync::Arc; use std::sync::atomic::{Ordering, AtomicBool}; use cgmath; +use font::{self, Rasterizer, RasterizedGlyph, FontDesc, GlyphKey, FontKey}; use gl::types::*; use gl; -use grid::Grid; use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op}; -use term::{self, cell, Cell}; -use font::{Rasterizer, RasterizedGlyph, FontDesc}; +use config::Config; +use grid::Grid; +use term::{self, cell, Cell}; use super::Rgb; @@ -84,58 +85,110 @@ pub struct Glyph { /// representations of the same code point. pub struct GlyphCache { /// Cache of buffered glyphs - cache: HashMap<char, Glyph>, + cache: HashMap<GlyphKey, Glyph>, /// Rasterizer for loading new glyphs rasterizer: Rasterizer, - /// Font description - desc: FontDesc, + /// regular font + font_key: FontKey, - /// Font Size - size: f32, + /// italic font + italic_key: FontKey, + + /// bold font + bold_key: FontKey, + + /// font size + font_size: font::Size, } impl GlyphCache { - pub fn new(rasterizer: Rasterizer, desc: FontDesc, font_size: f32) -> GlyphCache { - GlyphCache { + pub fn new<L>(mut rasterizer: Rasterizer, config: &Config, loader: &mut L) -> GlyphCache + where L: LoadGlyph + { + let font = config.font(); + let size = font.size(); + + // Load regular font + let regular_desc = FontDesc::new(font.family(), font.style()); + let regular = rasterizer.load_font(®ular_desc, size).expect("regular font load ok"); + + // Load bold font + let bold_style = font.bold_style().unwrap_or("Bold"); + let bold_desc = FontDesc::new(font.family(), bold_style); + + let bold = if bold_desc == regular_desc { + regular.clone() + } else { + rasterizer.load_font(&bold_desc, size).unwrap_or_else(|| regular.clone()) + }; + + // Load italic font + let italic_style = font.italic_style().unwrap_or("Italic"); + let italic_desc = FontDesc::new(font.family(), italic_style); + + let italic = if italic_desc == regular_desc { + regular.clone() + } else { + rasterizer.load_font(&italic_desc, size) + .unwrap_or_else(|| regular.clone()) + }; + + let mut cache = GlyphCache { cache: HashMap::new(), rasterizer: rasterizer, - desc: desc, - size: font_size, + font_size: font.size(), + font_key: regular.clone(), + bold_key: bold.clone(), + italic_key: italic.clone(), + }; + + macro_rules! load_glyphs_for_font { + ($font:expr) => { + for i in 32u8...128u8 { + cache.load_and_cache_glyph(GlyphKey { + font_key: $font, + c: i as char, + size: font.size() + }, loader); + } + } } + + load_glyphs_for_font!(regular); + load_glyphs_for_font!(bold); + load_glyphs_for_font!(italic); + + cache } - pub fn init<L>(&mut self, loader: &mut L) - where L: LoadGlyph - { - for i in 32u8...128u8 { - self.load_and_cache_glyph(i as char, loader); - } + pub fn font_metrics(&self) -> font::Metrics { + self.rasterizer.metrics(self.font_key, self.font_size) } - fn load_and_cache_glyph<L>(&mut self, c: char, loader: &mut L) + fn load_and_cache_glyph<L>(&mut self, glyph_key: GlyphKey, loader: &mut L) where L: LoadGlyph { - let rasterized = self.rasterizer.get_glyph(&self.desc, self.size, c); + let rasterized = self.rasterizer.get_glyph(&glyph_key); let glyph = loader.load_glyph(&rasterized); - self.cache.insert(c, glyph); + self.cache.insert(glyph_key, glyph); } - pub fn get<L>(&mut self, c: char, loader: &mut L) -> Option<&Glyph> + pub fn get<L>(&mut self, glyph_key: &GlyphKey, loader: &mut L) -> Option<&Glyph> where L: LoadGlyph { // Return glyph if it's already loaded // hi borrowck { - if self.cache.contains_key(&c) { - return self.cache.get(&c); + if self.cache.contains_key(glyph_key) { + return self.cache.get(glyph_key); } } // Rasterize and load the glyph - self.load_and_cache_glyph(c, loader); - self.cache.get(&c) + self.load_and_cache_glyph(glyph_key.to_owned(), loader); + self.cache.get(&glyph_key) } } @@ -188,6 +241,12 @@ pub struct RenderApi<'a> { } #[derive(Debug)] +pub struct LoaderApi<'a> { + active_tex: &'a mut GLuint, + atlas: &'a mut Vec<Atlas>, +} + +#[derive(Debug)] pub struct PackedVertex { x: f32, y: f32, @@ -436,8 +495,8 @@ impl QuadRenderer { renderer } - pub fn with_api<F>(&mut self, props: &term::SizeInfo, mut func: F) - where F: FnMut(RenderApi) + pub fn with_api<F, T>(&mut self, props: &term::SizeInfo, func: F) -> T + where F: FnOnce(RenderApi) -> T { if self.should_reload.load(Ordering::Relaxed) { self.reload_shaders(props.width as u32, props.height as u32); @@ -453,7 +512,7 @@ impl QuadRenderer { gl::ActiveTexture(gl::TEXTURE0); } - func(RenderApi { + let res = func(RenderApi { active_tex: &mut self.active_tex, batch: &mut self.batch, atlas: &mut self.atlas, @@ -467,6 +526,21 @@ impl QuadRenderer { self.program.deactivate(); } + + res + } + + pub fn with_loader<F, T>(&mut self, func: F) -> T + where F: FnOnce(LoaderApi) -> T + { + unsafe { + gl::ActiveTexture(gl::TEXTURE0); + } + + func(LoaderApi { + active_tex: &mut self.active_tex, + atlas: &mut self.atlas, + }) } pub fn reload_shaders(&mut self, width: u32, height: u32) { @@ -545,7 +619,13 @@ impl<'a> RenderApi<'a> { let mut col = 100.0; for c in s.chars() { - if let Some(glyph) = glyph_cache.get(c, self) { + let glyph_key = GlyphKey { + font_key: glyph_cache.font_key, + size: glyph_cache.font_size, + c: c + }; + + if let Some(glyph) = glyph_cache.get(&glyph_key, self) { let cell = Cell { c: c, fg: *color, @@ -587,8 +667,23 @@ impl<'a> RenderApi<'a> { continue; } - // Add cell to batch if the glyph is laoded - if let Some(glyph) = glyph_cache.get(cell.c, self) { + // Get font key for cell + // FIXME this is super inefficient. + let mut font_key = glyph_cache.font_key; + if cell.flags.contains(cell::BOLD) { + font_key = glyph_cache.bold_key; + } else if cell.flags.contains(cell::ITALIC) { + font_key = glyph_cache.italic_key; + } + + let glyph_key = GlyphKey { + font_key: font_key, + size: glyph_cache.font_size, + c: cell.c + }; + + // Add cell to batch if glyph available + if let Some(glyph) = glyph_cache.get(&glyph_key, self) { self.add_render_item(i as f32, j as f32, cell, glyph); } } @@ -596,11 +691,30 @@ impl<'a> RenderApi<'a> { } } +impl<'a> LoadGlyph for LoaderApi<'a> { + /// Load a glyph into a texture atlas + /// + /// If the current atlas is full, a new one will be created. + fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { + // At least one atlas is guaranteed to be in the `self.atlas` list; thus the unwrap. + match self.atlas.last_mut().unwrap().insert(rasterized, &mut self.active_tex) { + Ok(glyph) => glyph, + Err(_) => { + let atlas = Atlas::new(ATLAS_SIZE); + *self.active_tex = 0; // Atlas::new binds a texture. Ugh this is sloppy. + self.atlas.push(atlas); + self.load_glyph(rasterized) + } + } + } +} + impl<'a> LoadGlyph for RenderApi<'a> { /// Load a glyph into a texture atlas /// /// If the current atlas is full, a new one will be created. fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { + // At least one atlas is guaranteed to be in the `self.atlas` list; thus the unwrap. match self.atlas.last_mut().unwrap().insert(rasterized, &mut self.active_tex) { Ok(glyph) => glyph, Err(_) => { @@ -656,7 +770,7 @@ impl ShaderProgram { assert!($uniform != gl::INVALID_OPERATION as i32); }; ( $( $uniform:expr ),* ) => { - $( assert_uniform_valid!($uniform) )* + $( assert_uniform_valid!($uniform); )* }; } |