diff --git a/Cargo.lock b/Cargo.lock index 7b2c437ef..43756ce81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -672,6 +672,7 @@ name = "config" version = "0.1.0" dependencies = [ "anyhow", + "bitflags", "bstr 0.2.15", "dirs-next", "filenamegen", diff --git a/config/Cargo.toml b/config/Cargo.toml index e89c760d5..ec91755d6 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -15,6 +15,7 @@ pretty_env_logger = "0.4" [dependencies] anyhow = "1.0" +bitflags = "1.0" bstr = "0.2" dirs-next = "2.0" filenamegen = "0.2" diff --git a/config/src/font.rs b/config/src/font.rs index 3531a0c1f..274d79cb0 100644 --- a/config/src/font.rs +++ b/config/src/font.rs @@ -1,7 +1,88 @@ use crate::*; +use bitflags::*; use luahelper::impl_lua_conversion; +use serde::{Deserialize, Deserializer, Serialize}; use termwiz::color::RgbColor; +#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FreeTypeLoadTarget { + /// This corresponds to the default hinting algorithm, optimized + /// for standard gray-level rendering. + Normal, + /// A lighter hinting algorithm for non-monochrome modes. Many + /// generated glyphs are more fuzzy but better resemble its + /// original shape. A bit like rendering on Mac OS X. This target + /// implies FT_LOAD_FORCE_AUTOHINT. + Light, + /// Strong hinting algorithm that should only be used for + /// monochrome output. The result is probably unpleasant if the + /// glyph is rendered in non-monochrome modes. + Mono, + /// A variant of Normal optimized for horizontally decimated LCD displays. + HorizontalLcd, + /// A variant of Normal optimized for vertically decimated LCD displays. + VerticalLcd, +} + +impl Default for FreeTypeLoadTarget { + fn default() -> Self { + Self::Normal + } +} + +bitflags! { + // Note that these are strongly coupled with deps/freetype/src/lib.rs, + // but we can't directly reference that from here without making config + // depend on freetype. + #[derive(Default, Deserialize, Serialize)] + pub struct FreeTypeLoadFlags: u32 { + /// FT_LOAD_DEFAULT + const DEFAULT = 0; + /// Disable hinting. This generally generates ‘blurrier’ + /// bitmap glyph when the glyph is rendered in any of the + /// anti-aliased modes. See also the note below. This flag is + /// implied by FT_LOAD_NO_SCALE. + const NO_HINTING = 2; + const NO_BITMAP = 8; + /// Indicates that the auto-hinter is preferred over the + /// font’s native hinter. + const FORCE_AUTOHINT = 32; + const MONOCHROME = 4096; + /// Disable auto-hinter. + const NO_AUTOHINT = 32768; + } +} + +impl FreeTypeLoadFlags { + pub fn de_string<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + let mut flags = FreeTypeLoadFlags::default(); + + for ele in s.split('|') { + let ele = ele.trim(); + match ele { + "DEFAULT" => flags |= Self::DEFAULT, + "NO_HINTING" => flags |= Self::NO_HINTING, + "NO_BITMAP" => flags |= Self::NO_BITMAP, + "FORCE_AUTOHINT" => flags |= Self::FORCE_AUTOHINT, + "MONOCHROME" => flags |= Self::MONOCHROME, + "NO_AUTOHINT" => flags |= Self::NO_AUTOHINT, + _ => { + return Err(serde::de::Error::custom(format!( + "invalid FreeTypeLoadFlags {} in {}", + ele, s + ))); + } + } + } + + Ok(flags) + } +} + #[derive(Debug, Copy, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)] pub enum FontHinting { /// No hinting is performed diff --git a/config/src/lib.rs b/config/src/lib.rs index b80d34cb8..c45871e14 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -571,6 +571,11 @@ pub struct Config { #[serde(default)] pub font_antialias: FontAntiAliasing, + #[serde(default)] + pub freetype_load_target: FreeTypeLoadTarget, + #[serde(default, deserialize_with = "FreeTypeLoadFlags::de_string")] + pub freetype_load_flags: FreeTypeLoadFlags, + /// Selects the freetype interpret version to use. /// Likely values are 35, 38 and 40 which have different /// characteristics with respective to subpixel hinting. diff --git a/wezterm-font/src/ftwrap.rs b/wezterm-font/src/ftwrap.rs index 26f22222f..5729e8bce 100644 --- a/wezterm-font/src/ftwrap.rs +++ b/wezterm-font/src/ftwrap.rs @@ -2,7 +2,7 @@ use crate::locator::FontDataHandle; use anyhow::{anyhow, Context}; -use config::{configuration, FontAntiAliasing, FontHinting}; +use config::{configuration, FreeTypeLoadTarget}; pub use freetype::*; use std::ptr; @@ -44,33 +44,18 @@ fn render_mode_to_load_target(render_mode: FT_Render_Mode) -> u32 { pub fn compute_load_flags_from_config() -> (i32, FT_Render_Mode) { let config = configuration(); - let render = match config.font_antialias { - FontAntiAliasing::None => FT_Render_Mode::FT_RENDER_MODE_MONO, - FontAntiAliasing::Greyscale => FT_Render_Mode::FT_RENDER_MODE_NORMAL, - FontAntiAliasing::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD, + let load_flags = config.freetype_load_flags.bits() | FT_LOAD_COLOR; + let render = match config.freetype_load_target { + FreeTypeLoadTarget::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO, + FreeTypeLoadTarget::Normal => FT_Render_Mode::FT_RENDER_MODE_NORMAL, + FreeTypeLoadTarget::Light => FT_Render_Mode::FT_RENDER_MODE_LIGHT, + FreeTypeLoadTarget::HorizontalLcd => FT_Render_Mode::FT_RENDER_MODE_LCD, + FreeTypeLoadTarget::VerticalLcd => FT_Render_Mode::FT_RENDER_MODE_LCD_V, }; - let flags = match config.font_hinting { - FontHinting::None => { - render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_NORMAL) | FT_LOAD_NO_HINTING - } - FontHinting::Vertical => render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_LIGHT), - FontHinting::VerticalSubpixel | FontHinting::Full => { - render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_LCD) - } - }; + let load_flags = load_flags | render_mode_to_load_target(render); - // If the bitmaps are in color, we want those! - let flags = flags | FT_LOAD_COLOR; - - let flags = if config.font_antialias == FontAntiAliasing::None { - // When AA is disabled, force outline rendering to monochrome - flags | FT_LOAD_MONOCHROME - } else { - flags - } as i32; - - (flags, render) + (load_flags as i32, render) } pub struct Face {