1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-11 14:25:57 +03:00

fonts: can now synthesize italics for bitmap fonts

refs: #815
This commit is contained in:
Wez Furlong 2021-05-23 08:27:36 -07:00
parent c37ee01222
commit 0519b5499a
3 changed files with 71 additions and 3 deletions

View File

@ -24,7 +24,7 @@ As features stabilize some brief notes about them will accumulate here.
* Fixed: errors loading custom color schemes are now logged to the error log [#794](https://github.com/wez/wezterm/issues/794)
* Fixed: OSC 7 (current working directory) now works with paths that contain spaces and other special characters. Thanks to [@Arvedui](https://github.com/Arvedui)! [#799](https://github.com/wez/wezterm/pull/799)
* Changed: the homebrew tap is now a Cask that installs to the /Applications directory on macOS. Thanks to [@laggardkernel](https://github.com/laggardkernel)!
* New: bold and/or italics are now synthesized for fonts when the matching font is not actually italic or doesn't match the requested weight. Synthesized italics only apply to scalable fonts. [#815](https://github.com/wez/wezterm/issues/815)
* New: bold and/or italics are now synthesized for fonts when the matching font is not actually italic or doesn't match the requested weight. [#815](https://github.com/wez/wezterm/issues/815)
### 20210502-154244-3f7122cb

View File

@ -2,7 +2,7 @@ use crate::parser::ParsedFont;
use crate::rasterizer::FontRasterizer;
use crate::units::*;
use crate::{ftwrap, RasterizedGlyph};
use ::freetype::{FT_GlyphSlotRec_, FT_Matrix};
use ::freetype::{FT_GlyphSlotRec_, FT_Glyph_Format, FT_Matrix};
use anyhow::bail;
use std::cell::RefCell;
use std::mem;
@ -14,6 +14,7 @@ pub struct FreeTypeRasterizer {
face: RefCell<ftwrap::Face>,
_lib: ftwrap::Library,
synthesize_bold: bool,
synthesize_italic: bool,
}
impl FontRasterizer for FreeTypeRasterizer {
@ -53,7 +54,14 @@ impl FontRasterizer for FreeTypeRasterizer {
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_MONO => self.rasterize_mono(pitch, ft_glyph, data),
mode => bail!("unhandled pixel mode: {:?}", mode),
};
Ok(glyph)
let slot = unsafe { &mut *(*face.face).glyph };
if self.synthesize_italic && slot.format == FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP {
// The source was and thus the italic transform did nothing
Ok(glyph.skew())
} else {
Ok(glyph)
}
}
}
@ -313,6 +321,7 @@ impl FreeTypeRasterizer {
face: RefCell::new(face),
has_color,
synthesize_bold: parsed.synthesize_bold,
synthesize_italic: parsed.synthesize_italic,
})
}
}

View File

@ -16,6 +16,65 @@ pub struct RasterizedGlyph {
pub has_color: bool,
}
impl RasterizedGlyph {
/// Computes a skewed version of this glyph to produce a synthesized oblique variant
pub fn skew(&self) -> Self {
// This function is derived from code which is subject to the terms of the
// Mozilla Public License, v. 2.0. http://mozilla.org/MPL/2.0/
let factor = 0.2f64; // Skew factor
let stride = self.width * 4;
// Calculate the skewed horizontal offsets of the bottom and top of the glyph.
let bottom = self.bearing_y.get().round() as f64 - self.height as f64;
let skew_min = ((bottom + 0.5) * factor).floor();
let skew_max = ((self.bearing_y.get() as f64 - 0.5) * factor).ceil();
// Allocate enough extra width for the min/max skew offsets.
let skew_width = self.width + (skew_max - skew_min) as usize;
let mut skew_buffer = vec![0u8; skew_width * self.height * 4];
for y in 0..self.height {
// Calculate a skew offset at the vertical center of the current row.
let offset = (self.bearing_y.get() - y as f64 - 0.5) * factor - skew_min;
// Get a blend factor in 0..256 constant across all pixels in the row.
let blend = (offset.fract() * 256.0) as u32;
let src_row = y * stride;
let dest_row = (y * skew_width + offset.floor() as usize) * 4;
let mut prev_px = [0u32; 4];
for (src, dest) in self.data[src_row..src_row + stride]
.chunks(4)
.zip(skew_buffer[dest_row..dest_row + stride].chunks_mut(4))
{
let px = [src[0] as u32, src[1] as u32, src[2] as u32, src[3] as u32];
// Blend current pixel with previous pixel based on blend factor.
let next_px = [px[0] * blend, px[1] * blend, px[2] * blend, px[3] * blend];
dest[0] = ((((px[0] << 8) - next_px[0]) + prev_px[0] + 128) >> 8) as u8;
dest[1] = ((((px[1] << 8) - next_px[1]) + prev_px[1] + 128) >> 8) as u8;
dest[2] = ((((px[2] << 8) - next_px[2]) + prev_px[2] + 128) >> 8) as u8;
dest[3] = ((((px[3] << 8) - next_px[3]) + prev_px[3] + 128) >> 8) as u8;
// Save the remainder for blending onto the next pixel.
prev_px = next_px;
}
// If the skew misaligns the final pixel, write out the remainder.
if blend > 0 {
let dest = &mut skew_buffer[dest_row + stride..dest_row + stride + 4];
dest[0] = ((prev_px[0] + 128) >> 8) as u8;
dest[1] = ((prev_px[1] + 128) >> 8) as u8;
dest[2] = ((prev_px[2] + 128) >> 8) as u8;
dest[3] = ((prev_px[3] + 128) >> 8) as u8;
}
}
Self {
data: skew_buffer,
height: self.height,
width: skew_width,
bearing_x: self.bearing_x + PixelLength::new(skew_min),
bearing_y: self.bearing_y,
has_color: self.has_color,
}
}
}
/// Rasterizes the specified glyph index in the associated font
/// and returns the generated bitmap
pub trait FontRasterizer {