1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-10 06:34:17 +03:00

Add font hinting and antialiasing config options

Adds the following options for the top level configuration,
which allow manipulating the quality of hinting and AA that
occurs for freetype:

```
font_antialias = "Subpixel" # None, Greyscale, Subpixel
font_hinting = "Full" # None, Vertical, VerticalSubpixel, Full
```

Refs: https://github.com/wez/wezterm/issues/79
This commit is contained in:
Wez Furlong 2019-12-19 09:13:29 -08:00
parent 726f1421b4
commit fc9e5ea0f5
5 changed files with 90 additions and 26 deletions

View File

@ -8,6 +8,39 @@ const FONT_FAMILY: &str = "Consolas";
#[cfg(all(not(target_os = "macos"), not(windows)))]
const FONT_FAMILY: &str = "monospace";
#[derive(Debug, Copy, Deserialize, Clone, PartialEq, Eq, Hash)]
pub enum FontHinting {
/// No hinting is performed
None,
/// Light vertical hinting is performed to fit the terminal grid
Vertical,
/// Vertical hinting is performed, with additional processing
/// for subpixel anti-aliasing.
/// This level is equivalent to Microsoft ClearType.
VerticalSubpixel,
/// Vertical and horizontal hinting is performed.
Full,
}
impl Default for FontHinting {
fn default() -> Self {
Self::Full
}
}
#[derive(Debug, Copy, Deserialize, Clone, PartialEq, Eq, Hash)]
pub enum FontAntiAliasing {
None,
Greyscale,
Subpixel,
}
impl Default for FontAntiAliasing {
fn default() -> Self {
Self::Subpixel
}
}
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct FontAttributes {
/// The font family name

View File

@ -327,6 +327,10 @@ pub struct Config {
pub font_rasterizer: FontRasterizerSelection,
#[serde(default)]
pub font_shaper: FontShaperSelection,
#[serde(default)]
pub font_hinting: FontHinting,
#[serde(default)]
pub font_antialias: FontAntiAliasing,
#[serde(default)]
pub front_end: FrontEndSelection,

View File

@ -1,7 +1,8 @@
//! Higher level freetype bindings
use crate::config::{configuration, FontAntiAliasing, FontHinting};
use crate::font::locator::FontDataHandle;
use anyhow::{anyhow, bail, Context};
use anyhow::{anyhow, Context};
pub use freetype::*;
use std::ptr;
@ -19,13 +20,44 @@ fn ft_result<T>(err: FT_Error, t: T) -> anyhow::Result<T> {
}
}
pub fn compute_load_flags_for_mode(render_mode: FT_Render_Mode) -> i32 {
FT_LOAD_COLOR as i32 |
// enable FT_LOAD_TARGET bits. There are no flags defined
// for these in the bindings so we do some bit magic for
// ourselves. This is how the FT_LOAD_TARGET_() macro
// assembles these bits.
(render_mode as i32) << 16
fn render_mode_to_load_target(render_mode: FT_Render_Mode) -> u32 {
// enable FT_LOAD_TARGET bits. There are no flags defined
// for these in the bindings so we do some bit magic for
// ourselves. This is how the FT_LOAD_TARGET_() macro
// assembles these bits.
(render_mode as u32) & 15 << 16
}
pub fn compute_load_flags_from_config() -> i32 {
let config = configuration();
let flags = match (config.font_hinting, config.font_antialias) {
(FontHinting::VerticalSubpixel, _) | (_, FontAntiAliasing::Subpixel) => {
render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_LCD)
}
(FontHinting::None, _) => {
render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_NORMAL) | FT_LOAD_NO_HINTING
}
(FontHinting::Vertical, FontAntiAliasing::None)
| (FontHinting::Full, FontAntiAliasing::None) => {
render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_MONO)
}
(FontHinting::Vertical, _) => {
render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_LIGHT)
}
(FontHinting::Full, _) => render_mode_to_load_target(FT_Render_Mode::FT_RENDER_MODE_NORMAL),
};
// 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
}
pub struct Face {
@ -116,16 +148,13 @@ impl Face {
&mut self,
glyph_index: FT_UInt,
load_flags: FT_Int32,
render_mode: FT_Render_Mode,
) -> anyhow::Result<&FT_GlyphSlotRec_> {
unsafe {
let res = FT_Load_Glyph(self.face, glyph_index, load_flags);
if succeeded(res) {
let render = FT_Render_Glyph((*self.face).glyph, render_mode);
if !succeeded(render) {
bail!("FT_Render_Glyph failed: {:?}", render);
}
}
let res = FT_Load_Glyph(
self.face,
glyph_index,
(FT_LOAD_DEFAULT | FT_LOAD_RENDER) as i32 | load_flags,
);
ft_result(res, &*(*self.face).glyph)
}
}
@ -174,8 +203,11 @@ impl Library {
let lib = ft_result(res, lib).context("FT_Init_FreeType")?;
let mut lib = Library { lib };
// Some systems don't support this mode, so if it fails, we don't
// care to abort the rest of what we're doing
// Due to patent concerns, the freetype library disables the LCD
// filtering feature by default, and since we always build our
// own copy of freetype, it is likewise disabled by default for
// us too. As a result, this call will generally fail.
// Freetype is still able to render a decent result without it!
lib.set_lcd_filter(FT_LcdFilter::FT_LCD_FILTER_DEFAULT).ok();
Ok(lib)

View File

@ -23,15 +23,11 @@ impl FontRasterizer for FreeTypeRasterizer {
) -> anyhow::Result<RasterizedGlyph> {
self.face.borrow_mut().set_font_size(size, dpi)?;
let render_mode = //ftwrap::FT_Render_Mode::FT_RENDER_MODE_NORMAL;
// ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD;
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT;
let load_flags = ftwrap::compute_load_flags_for_mode(render_mode);
let load_flags = ftwrap::compute_load_flags_from_config();
let mut face = self.face.borrow_mut();
let descender = unsafe { (*(*face.face).size).metrics.descender as f64 / 64.0 };
let ft_glyph = face.load_and_render_glyph(glyph_pos, load_flags, render_mode)?;
let ft_glyph = face.load_and_render_glyph(glyph_pos, load_flags)?;
let mode: ftwrap::FT_Pixel_Mode =
unsafe { mem::transmute(u32::from(ft_glyph.bitmap.pixel_mode)) };

View File

@ -69,8 +69,7 @@ impl HarfbuzzShaper {
log::trace!("shaper wants {} {:?}", font_idx, &self.handles[font_idx]);
let face = self.lib.face_from_locator(&self.handles[font_idx])?;
let mut font = harfbuzz::Font::new(face.face);
let render_mode = ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT;
let load_flags = ftwrap::compute_load_flags_for_mode(render_mode);
let load_flags = ftwrap::compute_load_flags_from_config();
font.set_load_flags(load_flags);
*opt_pair = Some(FontPair {
face,