From e6c9c5036d878063e92922ea5e0a2e94e238741b Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 26 Oct 2017 20:01:17 -0700 Subject: [PATCH] [WIP] Add antialias option Still a WIP because of unimplemented Linux portion. Support has been added for font rasterization options. At this time, the only supported option is antialias. It's a tri-state flag which can be either ~ for "use default", true to force antialias, and false to force disable antialias. The options may be specified both globally and per font section. The per font sections override global options. An example of a complex config follows. In the example, the global value requests using default antialias settings, and the normal face disables antialias. font: options: antialias: ~ normal: family: monospace options: antialias: false Finally, note that the top level font.options is used for fallback fonts. --- Cargo.lock | 2 + Cargo.toml | 2 +- alacritty.yml | 16 ++++++-- alacritty_macos.yml | 7 ++++ font/Cargo.toml | 6 +++ font/src/darwin/mod.rs | 92 +++++++++++++++++++++++++----------------- font/src/lib.rs | 27 ++++++++++++- src/config.rs | 12 +++++- src/display.rs | 6 ++- src/renderer/mod.rs | 10 ++--- 10 files changed, 130 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61404aeb..faab4cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -373,6 +373,8 @@ dependencies = [ "freetype-rs 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig 0.4.0 (git+https://github.com/jwilm/rust-fontconfig?branch=updated-2017-10-8)", ] diff --git a/Cargo.toml b/Cargo.toml index e983e3a2..97a84487 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ libc = "0.2.30" cgmath = "0.15" notify = "4" bitflags = "0.9.1" -font = { path = "./font" } +font = { path = "./font", features = ["with-serde"] } errno = "0.2.3" parking_lot = "0.4.5" serde = "1" diff --git a/alacritty.yml b/alacritty.yml index d2607051..d1607fc1 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -53,21 +53,31 @@ draw_bold_text_with_bright_colors: true # * https://wiki.archlinux.org/index.php/font_configuration#Fontconfig_configuration # * file:///usr/share/doc/fontconfig/fontconfig-user.html font: + # Default rasterization options + # + # These settings are used for fallback fonts *and* for normal/bold/italic + # fonts when they don't specify their own `options`. + # + # If you prefer to respect fontconfig settings, set the value of these + # properties to ~. + options: + antialias: true + # The normal (roman) font face to use. normal: - family: monospace # should be "Menlo" or something on macOS. + family: monospace # Style can be specified to pick a specific face. # style: Regular # The bold font face bold: - family: monospace # should be "Menlo" or something on macOS. + family: monospace # Style can be specified to pick a specific face. # style: Bold # The italic font face italic: - family: monospace # should be "Menlo" or something on macOS. + family: monospace # Style can be specified to pick a specific face. # style: Italic diff --git a/alacritty_macos.yml b/alacritty_macos.yml index 761542a1..0b7c5130 100644 --- a/alacritty_macos.yml +++ b/alacritty_macos.yml @@ -34,6 +34,13 @@ draw_bold_text_with_bright_colors: true # Font configuration (changes require restart) font: + # Default rasterization options + # + # These settings are used for fallback fonts *and* for normal/bold/italic + # fonts when they don't specify their own `options`. + options: + antialias: true + # The normal (roman) font face to use. normal: family: Menlo diff --git a/font/Cargo.toml b/font/Cargo.toml index 2a97f0bd..e67cdaf2 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -5,11 +5,16 @@ authors = ["Joe Wilm "] description = "Font rendering using the best available solution per platform" license = "Apache-2.0" +[features] +with-serde = ["serde", "serde_derive"] + [dependencies] euclid = "0.12.0" libc = "0.2" foreign-types = "0.2.0" log = "0.3" +serde = { version = "1", optional = true } +serde_derive = { version = "1", optional = true } [target.'cfg(not(target_os = "macos"))'.dependencies] servo-fontconfig = { git = "https://github.com/jwilm/rust-fontconfig", branch = "updated-2017-10-8" } @@ -20,3 +25,4 @@ core-foundation = "0.3.0" core-text = "6.1.0" core-graphics = "0.8.1" core-foundation-sys = "0.3.1" + diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 70b9e1f3..73a91565 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -43,6 +43,7 @@ use euclid::rect::Rect; use euclid::size::Size2D; use super::{FontDesc, RasterizedGlyph, Metrics, FontKey, GlyphKey}; +use ::Options; pub mod byte_order; use self::byte_order::kCGBitmapByteOrder32Host; @@ -85,6 +86,7 @@ pub struct Rasterizer { keys: HashMap<(FontDesc, Size), FontKey>, device_pixel_ratio: f32, use_thin_strokes: bool, + options: ::Options, } /// Errors occurring when using the core text rasterizer @@ -130,13 +132,18 @@ impl ::std::fmt::Display for Error { impl ::Rasterize for Rasterizer { type Err = Error; - fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result { + fn new( + options: Options, + device_pixel_ratio: f32, + use_thin_strokes: bool + ) -> Result { info!("device_pixel_ratio: {}", device_pixel_ratio); Ok(Rasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio: device_pixel_ratio, use_thin_strokes: use_thin_strokes, + options: options, }) } @@ -149,12 +156,18 @@ impl ::Rasterize for Rasterizer { Ok(font.metrics()) } - fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result { + fn load_font( + &mut self, + desc: &FontDesc, + size: Size, + font_options: &Options, + ) -> Result { self.keys .get(&(desc.to_owned(), size)) .map(|k| Ok(*k)) .unwrap_or_else(|| { - let mut font = self.get_font(desc, size)?; + let options = self.options.clone().merge(&font_options); + let mut font = self.get_font(desc, size, options)?; // TODO, we can't use apple's proposed // .Apple Symbol Fallback (filtered out below), @@ -166,7 +179,8 @@ impl ::Rasterize for Rasterizer { let symbols = { let fallback_style = Style::Description { slant:Slant::Normal, weight:Weight::Normal } ; let d = FontDesc::new("Apple Symbols".to_owned(), fallback_style); - self.get_font(&d, size)? + let options = self.options.clone(); + self.get_font(&d, size, options)? }; font.fallbacks.push(symbols); } @@ -209,14 +223,16 @@ impl Rasterizer { &mut self, desc: &FontDesc, style: &str, - size: Size + size: Size, + options: Options, ) -> Result { let descriptors = descriptors_for_family(&desc.name[..]); for descriptor in descriptors { if descriptor.style_name == style { // Found the font we want let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; - let font = descriptor.to_font(scaled_size, true); + let mut font = descriptor.to_font(scaled_size, options); + font.load_fallbacks(scaled_size, &self.options); return Ok(font); } } @@ -229,7 +245,8 @@ impl Rasterizer { desc: &FontDesc, slant: Slant, weight: Weight, - size: Size + size: Size, + options: Options, ) -> Result { let bold = match weight { Weight::Bold => true, @@ -243,7 +260,8 @@ impl Rasterizer { let descriptors = descriptors_for_family(&desc.name[..]); for descriptor in descriptors { - let font = descriptor.to_font(scaled_size, true); + let mut font = descriptor.to_font(scaled_size, options.clone()); + font.load_fallbacks(scaled_size, &self.options); if font.is_bold() == bold && font.is_italic() == italic { // Found the font we want return Ok(font); @@ -253,11 +271,11 @@ impl Rasterizer { Err(Error::MissingFont(desc.to_owned())) } - fn get_font(&mut self, desc: &FontDesc, size: Size) -> Result { + fn get_font(&mut self, desc: &FontDesc, size: Size, options: Options) -> Result { match desc.style { - Style::Specific(ref style) => self.get_specific_face(desc, style, size), + Style::Specific(ref style) => self.get_specific_face(desc, style, size, options), Style::Description { slant, weight } => { - self.get_matching_face(desc, slant, weight, size) + self.get_matching_face(desc, slant, weight, size, options) }, } } @@ -299,6 +317,7 @@ pub struct Font { ct_font: CTFont, cg_font: CGFont, fallbacks: Vec, + antialias: bool, } unsafe impl Send for Font {} @@ -370,31 +389,15 @@ pub fn descriptors_for_family(family: &str) -> Vec { impl Descriptor { /// Create a Font from this descriptor - pub fn to_font(&self, size: f64, load_fallbacks:bool) -> Font { + pub fn to_font(&self, size: f64, options: Options) -> Font { let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size); let cg_font = ct_font.copy_to_CGFont(); - let fallbacks = if load_fallbacks { - // TODO fixme, hardcoded en for english - cascade_list_for_languages(&ct_font, &vec!["en".to_owned()]) - .into_iter() - // the system lists contains (at least) two strange fonts: - // .Apple Symbol Fallback - // .Noto Sans Universal - // both have a .-prefix (to indicate they are internal?) - // neither work very well. the latter even breaks things because - // it defines code points with just [?] glyphs. - .filter(|desc| desc.font_path != "") - .map(|desc| desc.to_font(size, false)) - .collect() - } else { - vec![] - }; - Font { ct_font: ct_font, cg_font: cg_font, - fallbacks: fallbacks, + fallbacks: Vec::new(), + antialias: options.antialias.unwrap_or(true), } } } @@ -505,12 +508,12 @@ impl Font { cg_context.set_allows_font_smoothing(true); cg_context.set_should_smooth_fonts(true); - cg_context.set_allows_font_subpixel_quantization(true); - cg_context.set_should_subpixel_quantize_fonts(true); - cg_context.set_allows_font_subpixel_positioning(true); - cg_context.set_should_subpixel_position_fonts(true); - cg_context.set_allows_antialiasing(true); - cg_context.set_should_antialias(true); + cg_context.set_allows_font_subpixel_quantization(self.antialias); + cg_context.set_should_subpixel_quantize_fonts(self.antialias); + cg_context.set_allows_font_subpixel_positioning(self.antialias); + cg_context.set_should_subpixel_position_fonts(self.antialias); + cg_context.set_allows_antialiasing(self.antialias); + cg_context.set_should_antialias(self.antialias); // Set fill color to white for drawing the glyph cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); @@ -563,6 +566,21 @@ impl Font { None } } + + pub fn load_fallbacks(&mut self, size: f64, options: &Options) { + // TODO fixme, hardcoded en for english + self.fallbacks = cascade_list_for_languages(&self.ct_font, &vec!["en".to_owned()]) + .into_iter() + // the system lists contains (at least) two strange fonts: + // .Apple Symbol Fallback + // .Noto Sans Universal + // both have a .-prefix (to indicate they are internal?) + // neither work very well. the latter even breaks things because + // it defines code points with just [?] glyphs. + .filter(|desc| desc.font_path != "") + .map(|desc| desc.to_font(size, options.clone())) + .collect() + } } #[cfg(test)] @@ -582,7 +600,7 @@ mod tests { // Check to_font let fonts = list.iter() - .map(|desc| desc.to_font(72., false)) + .map(|desc| desc.to_font(72., &Default::default())) .collect::>(); for font in fonts { diff --git a/font/src/lib.rs b/font/src/lib.rs index 513dacdd..18ef1949 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -33,6 +33,12 @@ extern crate core_graphics; #[cfg(target_os = "macos")] extern crate euclid; +#[cfg(feature = "with-serde")] +extern crate serde; +#[cfg_attr(feature = "with-serde", macro_use)] +#[cfg(feature = "with-serde")] +extern crate serde_derive; + extern crate libc; #[cfg(not(target_os = "macos"))] @@ -58,6 +64,12 @@ mod darwin; #[cfg(target_os = "macos")] pub use darwin::*; +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature="with-serde", derive(Deserialize))] +pub struct Options { + antialias: Option, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FontDesc { name: String, @@ -237,15 +249,26 @@ pub trait Rasterize { type Err: ::std::error::Error + Send + Sync + 'static; /// Create a new Rasterize - fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result + fn new(_: Options, device_pixel_ratio: f32, use_thin_strokes: bool) -> Result where Self: Sized; /// Get `Metrics` for the given `FontKey` fn metrics(&self, FontKey) -> Result; /// Load the font described by `FontDesc` and `Size` - fn load_font(&mut self, &FontDesc, Size) -> Result; + fn load_font(&mut self, &FontDesc, Size, &Options) -> Result; /// Rasterize the glyph described by `GlyphKey`. fn get_glyph(&mut self, &GlyphKey) -> Result; } + +impl Options { + /// Merge options from other into self. Values defined in both self and + /// other will prefer other. + pub fn merge(mut self, other: &Options) -> Self { + let default = self.antialias.take(); + self.antialias = other.antialias.or(default); + + self + } +} diff --git a/src/config.rs b/src/config.rs index 3f3b6942..8f4f4450 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,7 +14,7 @@ use std::time::Duration; use std::collections::HashMap; use ::Rgb; -use font::Size; +use font::{self, Size}; use serde_yaml; use serde::{self, de, Deserialize}; use serde::de::Error as SerdeError; @@ -1305,6 +1305,10 @@ impl DeserializeFromF32 for Size { /// doesn't provide complete config is Ok. #[derive(Debug, Deserialize, Clone)] pub struct Font { + /// Default font rasterization options + #[serde(default)] + pub options: font::Options, + /// Font family pub normal: FontDescription, @@ -1342,6 +1346,9 @@ fn default_italic_desc() -> FontDescription { pub struct FontDescription { pub family: String, pub style: Option, + /// Rasterization options for this font. + #[serde(default)] + pub options: font::Options, } impl FontDescription { @@ -1349,6 +1356,7 @@ impl FontDescription { FontDescription { family: family.into(), style: None, + options: Default::default(), } } } @@ -1389,6 +1397,7 @@ impl Font { impl Default for Font { fn default() -> Font { Font { + options: Default::default(), normal: FontDescription::new_with_family("Menlo"), bold: FontDescription::new_with_family("Menlo"), italic: FontDescription::new_with_family("Menlo"), @@ -1404,6 +1413,7 @@ impl Default for Font { impl Default for Font { fn default() -> Font { Font { + options: Default::default(), normal: FontDescription::new_with_family("monospace"), bold: FontDescription::new_with_family("monospace"), italic: FontDescription::new_with_family("monospace"), diff --git a/src/display.rs b/src/display.rs index fc5a5d40..52207ac1 100644 --- a/src/display.rs +++ b/src/display.rs @@ -212,7 +212,11 @@ impl Display { { let font = config.font().clone().with_size_delta(font_size_delta as f32); let dpr = window.hidpi_factor(); - let rasterizer = font::Rasterizer::new(dpr, config.use_thin_strokes())?; + let rasterizer = font::Rasterizer::new( + font.options.clone(), + dpr, + config.use_thin_strokes() + )?; // Initialize glyph cache let glyph_cache = { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index bb025875..7c970232 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -230,26 +230,26 @@ impl GlyphCache { let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal); let regular = rasterizer - .load_font(®ular_desc, size)?; + .load_font(®ular_desc, size, &font.normal.options)?; // helper to load a description if it is not the regular_desc - let mut load_or_regular = |desc:FontDesc| { + let mut load_or_regular = |desc:FontDesc, options:&font::Options| { if desc == regular_desc { regular } else { - rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular) + rasterizer.load_font(&desc, size, options).unwrap_or_else(|_| regular) } }; // Load bold font let bold_desc = Self::make_desc(&font.bold, font::Slant::Normal, font::Weight::Bold); - let bold = load_or_regular(bold_desc); + let bold = load_or_regular(bold_desc, &font.bold.options); // Load italic font let italic_desc = Self::make_desc(&font.italic, font::Slant::Italic, font::Weight::Normal); - let italic = load_or_regular(italic_desc); + let italic = load_or_regular(italic_desc, &font.italic.options); Ok((regular, bold, italic)) }