1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-18 13:55:23 -05:00

[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.
This commit is contained in:
Joe Wilm 2017-10-26 20:01:17 -07:00
parent 22ccd7b4b1
commit e6c9c5036d
10 changed files with 130 additions and 50 deletions

2
Cargo.lock generated
View file

@ -373,6 +373,8 @@ dependencies = [
"freetype-rs 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "servo-fontconfig 0.4.0 (git+https://github.com/jwilm/rust-fontconfig?branch=updated-2017-10-8)",
] ]

View file

@ -16,7 +16,7 @@ libc = "0.2.30"
cgmath = "0.15" cgmath = "0.15"
notify = "4" notify = "4"
bitflags = "0.9.1" bitflags = "0.9.1"
font = { path = "./font" } font = { path = "./font", features = ["with-serde"] }
errno = "0.2.3" errno = "0.2.3"
parking_lot = "0.4.5" parking_lot = "0.4.5"
serde = "1" serde = "1"

View file

@ -53,21 +53,31 @@ draw_bold_text_with_bright_colors: true
# * https://wiki.archlinux.org/index.php/font_configuration#Fontconfig_configuration # * https://wiki.archlinux.org/index.php/font_configuration#Fontconfig_configuration
# * file:///usr/share/doc/fontconfig/fontconfig-user.html # * file:///usr/share/doc/fontconfig/fontconfig-user.html
font: 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. # The normal (roman) font face to use.
normal: normal:
family: monospace # should be "Menlo" or something on macOS. family: monospace
# Style can be specified to pick a specific face. # Style can be specified to pick a specific face.
# style: Regular # style: Regular
# The bold font face # The bold font face
bold: bold:
family: monospace # should be "Menlo" or something on macOS. family: monospace
# Style can be specified to pick a specific face. # Style can be specified to pick a specific face.
# style: Bold # style: Bold
# The italic font face # The italic font face
italic: italic:
family: monospace # should be "Menlo" or something on macOS. family: monospace
# Style can be specified to pick a specific face. # Style can be specified to pick a specific face.
# style: Italic # style: Italic

View file

@ -34,6 +34,13 @@ draw_bold_text_with_bright_colors: true
# Font configuration (changes require restart) # Font configuration (changes require restart)
font: 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. # The normal (roman) font face to use.
normal: normal:
family: Menlo family: Menlo

View file

@ -5,11 +5,16 @@ authors = ["Joe Wilm <joe@jwilm.com>"]
description = "Font rendering using the best available solution per platform" description = "Font rendering using the best available solution per platform"
license = "Apache-2.0" license = "Apache-2.0"
[features]
with-serde = ["serde", "serde_derive"]
[dependencies] [dependencies]
euclid = "0.12.0" euclid = "0.12.0"
libc = "0.2" libc = "0.2"
foreign-types = "0.2.0" foreign-types = "0.2.0"
log = "0.3" log = "0.3"
serde = { version = "1", optional = true }
serde_derive = { version = "1", optional = true }
[target.'cfg(not(target_os = "macos"))'.dependencies] [target.'cfg(not(target_os = "macos"))'.dependencies]
servo-fontconfig = { git = "https://github.com/jwilm/rust-fontconfig", branch = "updated-2017-10-8" } 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-text = "6.1.0"
core-graphics = "0.8.1" core-graphics = "0.8.1"
core-foundation-sys = "0.3.1" core-foundation-sys = "0.3.1"

View file

@ -43,6 +43,7 @@ use euclid::rect::Rect;
use euclid::size::Size2D; use euclid::size::Size2D;
use super::{FontDesc, RasterizedGlyph, Metrics, FontKey, GlyphKey}; use super::{FontDesc, RasterizedGlyph, Metrics, FontKey, GlyphKey};
use ::Options;
pub mod byte_order; pub mod byte_order;
use self::byte_order::kCGBitmapByteOrder32Host; use self::byte_order::kCGBitmapByteOrder32Host;
@ -85,6 +86,7 @@ pub struct Rasterizer {
keys: HashMap<(FontDesc, Size), FontKey>, keys: HashMap<(FontDesc, Size), FontKey>,
device_pixel_ratio: f32, device_pixel_ratio: f32,
use_thin_strokes: bool, use_thin_strokes: bool,
options: ::Options,
} }
/// Errors occurring when using the core text rasterizer /// Errors occurring when using the core text rasterizer
@ -130,13 +132,18 @@ impl ::std::fmt::Display for Error {
impl ::Rasterize for Rasterizer { impl ::Rasterize for Rasterizer {
type Err = Error; type Err = Error;
fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result<Rasterizer, Error> { fn new(
options: Options,
device_pixel_ratio: f32,
use_thin_strokes: bool
) -> Result<Rasterizer, Error> {
info!("device_pixel_ratio: {}", device_pixel_ratio); info!("device_pixel_ratio: {}", device_pixel_ratio);
Ok(Rasterizer { Ok(Rasterizer {
fonts: HashMap::new(), fonts: HashMap::new(),
keys: HashMap::new(), keys: HashMap::new(),
device_pixel_ratio: device_pixel_ratio, device_pixel_ratio: device_pixel_ratio,
use_thin_strokes: use_thin_strokes, use_thin_strokes: use_thin_strokes,
options: options,
}) })
} }
@ -149,12 +156,18 @@ impl ::Rasterize for Rasterizer {
Ok(font.metrics()) Ok(font.metrics())
} }
fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> { fn load_font(
&mut self,
desc: &FontDesc,
size: Size,
font_options: &Options,
) -> Result<FontKey, Error> {
self.keys self.keys
.get(&(desc.to_owned(), size)) .get(&(desc.to_owned(), size))
.map(|k| Ok(*k)) .map(|k| Ok(*k))
.unwrap_or_else(|| { .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 // TODO, we can't use apple's proposed
// .Apple Symbol Fallback (filtered out below), // .Apple Symbol Fallback (filtered out below),
@ -166,7 +179,8 @@ impl ::Rasterize for Rasterizer {
let symbols = { let symbols = {
let fallback_style = Style::Description { slant:Slant::Normal, weight:Weight::Normal } ; let fallback_style = Style::Description { slant:Slant::Normal, weight:Weight::Normal } ;
let d = FontDesc::new("Apple Symbols".to_owned(), fallback_style); 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); font.fallbacks.push(symbols);
} }
@ -209,14 +223,16 @@ impl Rasterizer {
&mut self, &mut self,
desc: &FontDesc, desc: &FontDesc,
style: &str, style: &str,
size: Size size: Size,
options: Options,
) -> Result<Font, Error> { ) -> Result<Font, Error> {
let descriptors = descriptors_for_family(&desc.name[..]); let descriptors = descriptors_for_family(&desc.name[..]);
for descriptor in descriptors { for descriptor in descriptors {
if descriptor.style_name == style { if descriptor.style_name == style {
// Found the font we want // Found the font we want
let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; 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); return Ok(font);
} }
} }
@ -229,7 +245,8 @@ impl Rasterizer {
desc: &FontDesc, desc: &FontDesc,
slant: Slant, slant: Slant,
weight: Weight, weight: Weight,
size: Size size: Size,
options: Options,
) -> Result<Font, Error> { ) -> Result<Font, Error> {
let bold = match weight { let bold = match weight {
Weight::Bold => true, Weight::Bold => true,
@ -243,7 +260,8 @@ impl Rasterizer {
let descriptors = descriptors_for_family(&desc.name[..]); let descriptors = descriptors_for_family(&desc.name[..]);
for descriptor in descriptors { 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 { if font.is_bold() == bold && font.is_italic() == italic {
// Found the font we want // Found the font we want
return Ok(font); return Ok(font);
@ -253,11 +271,11 @@ impl Rasterizer {
Err(Error::MissingFont(desc.to_owned())) Err(Error::MissingFont(desc.to_owned()))
} }
fn get_font(&mut self, desc: &FontDesc, size: Size) -> Result<Font, Error> { fn get_font(&mut self, desc: &FontDesc, size: Size, options: Options) -> Result<Font, Error> {
match desc.style { 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 } => { 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, ct_font: CTFont,
cg_font: CGFont, cg_font: CGFont,
fallbacks: Vec<Font>, fallbacks: Vec<Font>,
antialias: bool,
} }
unsafe impl Send for Font {} unsafe impl Send for Font {}
@ -370,31 +389,15 @@ pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
impl Descriptor { impl Descriptor {
/// Create a Font from this 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 ct_font = ct_new_from_descriptor(&self.ct_descriptor, size);
let cg_font = ct_font.copy_to_CGFont(); 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 { Font {
ct_font: ct_font, ct_font: ct_font,
cg_font: cg_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_allows_font_smoothing(true);
cg_context.set_should_smooth_fonts(true); cg_context.set_should_smooth_fonts(true);
cg_context.set_allows_font_subpixel_quantization(true); cg_context.set_allows_font_subpixel_quantization(self.antialias);
cg_context.set_should_subpixel_quantize_fonts(true); cg_context.set_should_subpixel_quantize_fonts(self.antialias);
cg_context.set_allows_font_subpixel_positioning(true); cg_context.set_allows_font_subpixel_positioning(self.antialias);
cg_context.set_should_subpixel_position_fonts(true); cg_context.set_should_subpixel_position_fonts(self.antialias);
cg_context.set_allows_antialiasing(true); cg_context.set_allows_antialiasing(self.antialias);
cg_context.set_should_antialias(true); cg_context.set_should_antialias(self.antialias);
// Set fill color to white for drawing the glyph // Set fill color to white for drawing the glyph
cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
@ -563,6 +566,21 @@ impl Font {
None 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)] #[cfg(test)]
@ -582,7 +600,7 @@ mod tests {
// Check to_font // Check to_font
let fonts = list.iter() let fonts = list.iter()
.map(|desc| desc.to_font(72., false)) .map(|desc| desc.to_font(72., &Default::default()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for font in fonts { for font in fonts {

View file

@ -33,6 +33,12 @@ extern crate core_graphics;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
extern crate euclid; 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; extern crate libc;
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
@ -58,6 +64,12 @@ mod darwin;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub use darwin::*; pub use darwin::*;
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature="with-serde", derive(Deserialize))]
pub struct Options {
antialias: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FontDesc { pub struct FontDesc {
name: String, name: String,
@ -237,15 +249,26 @@ pub trait Rasterize {
type Err: ::std::error::Error + Send + Sync + 'static; type Err: ::std::error::Error + Send + Sync + 'static;
/// Create a new Rasterize /// Create a new Rasterize
fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result<Self, Self::Err> fn new(_: Options, device_pixel_ratio: f32, use_thin_strokes: bool) -> Result<Self, Self::Err>
where Self: Sized; where Self: Sized;
/// Get `Metrics` for the given `FontKey` /// Get `Metrics` for the given `FontKey`
fn metrics(&self, FontKey) -> Result<Metrics, Self::Err>; fn metrics(&self, FontKey) -> Result<Metrics, Self::Err>;
/// Load the font described by `FontDesc` and `Size` /// Load the font described by `FontDesc` and `Size`
fn load_font(&mut self, &FontDesc, Size) -> Result<FontKey, Self::Err>; fn load_font(&mut self, &FontDesc, Size, &Options) -> Result<FontKey, Self::Err>;
/// Rasterize the glyph described by `GlyphKey`. /// Rasterize the glyph described by `GlyphKey`.
fn get_glyph(&mut self, &GlyphKey) -> Result<RasterizedGlyph, Self::Err>; fn get_glyph(&mut self, &GlyphKey) -> Result<RasterizedGlyph, Self::Err>;
} }
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
}
}

View file

@ -14,7 +14,7 @@ use std::time::Duration;
use std::collections::HashMap; use std::collections::HashMap;
use ::Rgb; use ::Rgb;
use font::Size; use font::{self, Size};
use serde_yaml; use serde_yaml;
use serde::{self, de, Deserialize}; use serde::{self, de, Deserialize};
use serde::de::Error as SerdeError; use serde::de::Error as SerdeError;
@ -1305,6 +1305,10 @@ impl DeserializeFromF32 for Size {
/// doesn't provide complete config is Ok. /// doesn't provide complete config is Ok.
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct Font { pub struct Font {
/// Default font rasterization options
#[serde(default)]
pub options: font::Options,
/// Font family /// Font family
pub normal: FontDescription, pub normal: FontDescription,
@ -1342,6 +1346,9 @@ fn default_italic_desc() -> FontDescription {
pub struct FontDescription { pub struct FontDescription {
pub family: String, pub family: String,
pub style: Option<String>, pub style: Option<String>,
/// Rasterization options for this font.
#[serde(default)]
pub options: font::Options,
} }
impl FontDescription { impl FontDescription {
@ -1349,6 +1356,7 @@ impl FontDescription {
FontDescription { FontDescription {
family: family.into(), family: family.into(),
style: None, style: None,
options: Default::default(),
} }
} }
} }
@ -1389,6 +1397,7 @@ impl Font {
impl Default for Font { impl Default for Font {
fn default() -> Font { fn default() -> Font {
Font { Font {
options: Default::default(),
normal: FontDescription::new_with_family("Menlo"), normal: FontDescription::new_with_family("Menlo"),
bold: FontDescription::new_with_family("Menlo"), bold: FontDescription::new_with_family("Menlo"),
italic: FontDescription::new_with_family("Menlo"), italic: FontDescription::new_with_family("Menlo"),
@ -1404,6 +1413,7 @@ impl Default for Font {
impl Default for Font { impl Default for Font {
fn default() -> Font { fn default() -> Font {
Font { Font {
options: Default::default(),
normal: FontDescription::new_with_family("monospace"), normal: FontDescription::new_with_family("monospace"),
bold: FontDescription::new_with_family("monospace"), bold: FontDescription::new_with_family("monospace"),
italic: FontDescription::new_with_family("monospace"), italic: FontDescription::new_with_family("monospace"),

View file

@ -212,7 +212,11 @@ impl Display {
{ {
let font = config.font().clone().with_size_delta(font_size_delta as f32); let font = config.font().clone().with_size_delta(font_size_delta as f32);
let dpr = window.hidpi_factor(); 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 // Initialize glyph cache
let glyph_cache = { let glyph_cache = {

View file

@ -230,26 +230,26 @@ impl GlyphCache {
let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal); let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal);
let regular = rasterizer let regular = rasterizer
.load_font(&regular_desc, size)?; .load_font(&regular_desc, size, &font.normal.options)?;
// helper to load a description if it is not the regular_desc // 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 { if desc == regular_desc {
regular regular
} else { } else {
rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular) rasterizer.load_font(&desc, size, options).unwrap_or_else(|_| regular)
} }
}; };
// Load bold font // Load bold font
let bold_desc = Self::make_desc(&font.bold, font::Slant::Normal, font::Weight::Bold); 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 // Load italic font
let italic_desc = Self::make_desc(&font.italic, font::Slant::Italic, font::Weight::Normal); 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)) Ok((regular, bold, italic))
} }