diff options
-rw-r--r-- | CHANGELOG.md | 6 | ||||
-rw-r--r-- | Cargo.lock | 53 | ||||
-rw-r--r-- | font/Cargo.toml | 3 | ||||
-rw-r--r-- | font/src/directwrite/mod.rs | 209 | ||||
-rw-r--r-- | font/src/lib.rs | 4 | ||||
-rw-r--r-- | font/src/rusttype/mod.rs | 161 |
6 files changed, 232 insertions, 204 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 55ce0af1..c32d58b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- On Windows, Alacritty will now use the native DirectWrite font API + ## Version 0.3.2 ### Fixed @@ -445,17 +445,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "core-graphics" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-graphics" version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -467,17 +456,6 @@ dependencies = [ [[package]] name = "core-text" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-text" version = "13.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -635,6 +613,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "dwrote" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "either" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -788,29 +778,16 @@ dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", - "font-loader 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rusttype 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "font-loader" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2898,9 +2875,7 @@ dependencies = [ "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e54c4ab33705fa1fc8af375bb7929d68e1c1546c1ecef408966d8c3e49a1d84a" "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" -"checksum core-text 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f59bff773954e5cd058a3f5983406b52bec7cc65202bef340ba64a0c40ac91" "checksum core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a72b5e50e549969dd88eff3047495fe5b8c6f028635442c2b708be707e669" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" @@ -2919,6 +2894,7 @@ dependencies = [ "checksum downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b92dfd5c2f75260cbf750572f95d387e7ca0ba5e3fbe9e1a33f23025be020f" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0ad6bf6a88548d1126045c413548df1453d9be094a8ab9fd59bf1fdd338da4f" +"checksum dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bd1369e02db5e9b842a9b67bce8a2fcc043beafb2ae8a799dd482d46ea1ff0d" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum embed-resource 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee11dd277e159f3a7845341f8c800899cf918a366956e31dd52f35eff638a403" "checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" @@ -2935,7 +2911,6 @@ dependencies = [ "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" "checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum font-loader 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ece0e8a5dd99a65f8de977b4a3f89e3b5a5259e15ae610952cdb894e96f5e2e" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28cc92a7040ee7b631e4279e263f9a83aedc1eb6085c68d8ca4d072b5644e705" diff --git a/font/Cargo.toml b/font/Cargo.toml index 8b471f37..e5515a8e 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -22,5 +22,4 @@ core-graphics = "0.17" core-foundation-sys = "0.6" [target.'cfg(windows)'.dependencies] -font-loader = "0.8.0" -rusttype = "0.7.5" +dwrote = { version = "0.9.0" } diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs new file mode 100644 index 00000000..0284b397 --- /dev/null +++ b/font/src/directwrite/mod.rs @@ -0,0 +1,209 @@ +// Copyright 2019 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//! Rasterization powered by DirectWrite +extern crate dwrote; +use self::dwrote::{ + FontCollection, FontStretch, FontStyle, FontWeight, GlyphOffset, GlyphRunAnalysis, +}; + +use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; + +pub struct DirectWriteRasterizer { + fonts: Vec<dwrote::FontFace>, + device_pixel_ratio: f32, +} + +impl crate::Rasterize for DirectWriteRasterizer { + type Err = Error; + + fn new(device_pixel_ratio: f32, _: bool) -> Result<DirectWriteRasterizer, Error> { + Ok(DirectWriteRasterizer { fonts: Vec::new(), device_pixel_ratio }) + } + + fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> { + let font = self.fonts.get(key.token as usize).ok_or(Error::FontNotLoaded)?; + + let vmetrics = font.metrics(); + let scale = (size.as_f32_pts() * self.device_pixel_ratio * (96.0 / 72.0)) + / f32::from(vmetrics.designUnitsPerEm); + + let underline_position = f32::from(vmetrics.underlinePosition) * scale; + let underline_thickness = f32::from(vmetrics.underlineThickness) * scale; + + let strikeout_position = f32::from(vmetrics.strikethroughPosition) * scale; + let strikeout_thickness = f32::from(vmetrics.strikethroughThickness) * scale; + + let ascent = f32::from(vmetrics.ascent) * scale; + let descent = -f32::from(vmetrics.descent) * scale; + let line_gap = f32::from(vmetrics.lineGap) * scale; + + let line_height = f64::from(ascent - descent + line_gap); + + // We assume that all monospace characters have the same width + // Because of this we take '!', the first drawable character, for measurements + let glyph_metrics = font.get_design_glyph_metrics(&[33], false); + let hmetrics = glyph_metrics.first().ok_or(Error::MissingGlyph('!'))?; + + let average_advance = f64::from(hmetrics.advanceWidth) * f64::from(scale); + + Ok(Metrics { + descent, + average_advance, + line_height, + underline_position, + underline_thickness, + strikeout_position, + strikeout_thickness, + }) + } + + fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> { + let system_fc = FontCollection::system(); + + let family = system_fc + .get_font_family_by_name(&desc.name) + .ok_or_else(|| Error::MissingFont(desc.clone()))?; + + let font = match desc.style { + Style::Description { weight, slant } => { + let weight = + if weight == Weight::Bold { FontWeight::Bold } else { FontWeight::Regular }; + + let style = match slant { + Slant::Normal => FontStyle::Normal, + Slant::Oblique => FontStyle::Oblique, + Slant::Italic => FontStyle::Italic, + }; + + // This searches for the "best" font - should mean we don't have to worry about + // fallbacks if our exact desired weight/style isn't available + Ok(family.get_first_matching_font(weight, FontStretch::Normal, style)) + }, + Style::Specific(ref style) => { + let mut idx = 0; + let count = family.get_font_count(); + + loop { + if idx == count { + break Err(Error::MissingFont(desc.clone())); + } + + let font = family.get_font(idx); + + if font.face_name() == *style { + break Ok(font); + } + + idx += 1; + } + }, + }?; + + let face = font.create_font_face(); + self.fonts.push(face); + + Ok(FontKey { token: (self.fonts.len() - 1) as u16 }) + } + + fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> { + let font = self.fonts.get(glyph.font_key.token as usize).ok_or(Error::FontNotLoaded)?; + + let offset = GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 }; + + let glyph_index = *font + .get_glyph_indices(&[glyph.c as u32]) + .first() + .ok_or_else(|| Error::MissingGlyph(glyph.c))?; + if glyph_index == 0 { + // The DirectWrite documentation states that we should get 0 returned if the glyph + // does not exist in the font + return Err(Error::MissingGlyph(glyph.c)); + } + + let glyph_run = dwrote::DWRITE_GLYPH_RUN { + fontFace: unsafe { font.as_ptr() }, + fontEmSize: glyph.size.as_f32_pts(), + glyphCount: 1, + glyphIndices: &(glyph_index), + glyphAdvances: &(0.0), + glyphOffsets: &(offset), + isSideways: 0, + bidiLevel: 0, + }; + + let glyph_analysis = GlyphRunAnalysis::create( + &glyph_run, + self.device_pixel_ratio * (96.0 / 72.0), + None, + dwrote::DWRITE_RENDERING_MODE_NATURAL, + dwrote::DWRITE_MEASURING_MODE_NATURAL, + 0.0, + 0.0, + ) + .or_else(|_| Err(Error::MissingGlyph(glyph.c)))?; + + let bounds = glyph_analysis + .get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1) + .or_else(|_| Err(Error::MissingGlyph(glyph.c)))?; + let buf = glyph_analysis + .create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds) + .or_else(|_| Err(Error::MissingGlyph(glyph.c)))?; + + Ok(RasterizedGlyph { + c: glyph.c, + width: (bounds.right - bounds.left) as i32, + height: (bounds.bottom - bounds.top) as i32, + top: -bounds.top, + left: bounds.left, + buf, + }) + } + + fn update_dpr(&mut self, device_pixel_ratio: f32) { + self.device_pixel_ratio = device_pixel_ratio; + } +} + +#[derive(Debug)] +pub enum Error { + MissingFont(FontDesc), + MissingGlyph(char), + FontNotLoaded, +} + +impl ::std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::MissingFont(ref _desc) => "Couldn't find the requested font", + Error::MissingGlyph(ref _c) => "Couldn't find the requested glyph", + Error::FontNotLoaded => "Tried to operate on font that hasn't been loaded", + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c), + Error::MissingFont(ref desc) => write!( + f, + "Couldn't find a font with {}\n\tPlease check the font config in your \ + alacritty.yml.", + desc + ), + Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), + } + } +} diff --git a/font/src/lib.rs b/font/src/lib.rs index 571bf104..d3bddd54 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -56,9 +56,9 @@ pub mod ft; pub use ft::{Error, FreeTypeRasterizer as Rasterizer}; #[cfg(windows)] -pub mod rusttype; +pub mod directwrite; #[cfg(windows)] -pub use crate::rusttype::{Error, RustTypeRasterizer as Rasterizer}; +pub use crate::directwrite::{DirectWriteRasterizer as Rasterizer, Error}; // If target is macos, reexport everything from darwin #[cfg(target_os = "macos")] diff --git a/font/src/rusttype/mod.rs b/font/src/rusttype/mod.rs deleted file mode 100644 index 8be9fe08..00000000 --- a/font/src/rusttype/mod.rs +++ /dev/null @@ -1,161 +0,0 @@ -extern crate font_loader; -use self::font_loader::system_fonts; - -extern crate rusttype; -use self::rusttype::{point, Codepoint, FontCollection, Scale}; - -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; - -pub struct RustTypeRasterizer { - fonts: Vec<rusttype::Font<'static>>, - dpi_ratio: f32, -} - -impl crate::Rasterize for RustTypeRasterizer { - type Err = Error; - - fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> { - Ok(RustTypeRasterizer { fonts: Vec::new(), dpi_ratio: device_pixel_ratio }) - } - - fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> { - let scale = Scale::uniform(size.as_f32_pts() * self.dpi_ratio * 96. / 72.); - let vmetrics = self.fonts[key.token as usize].v_metrics(scale); - let hmetrics = self.fonts[key.token as usize] - .glyph( - // If the font is monospaced all glyphs *should* have the same width - // 33 '!' is the first displaying character - Codepoint(33), - ) - .scaled(scale) - .h_metrics(); - - let line_height = f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap); - let average_advance = f64::from(hmetrics.advance_width); - let descent = vmetrics.descent; - - // Strikeout and underline metrics. - // RustType doesn't support these, so we make up our own. - let thickness = (descent / 5.).round(); - let underline_position = descent / 2.; - let strikeout_position = line_height as f32 / 2. - descent; - - Ok(Metrics { - descent, - average_advance, - line_height, - underline_position, - underline_thickness: thickness, - strikeout_position, - strikeout_thickness: thickness, - }) - } - - fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> { - let fp = system_fonts::FontPropertyBuilder::new().family(&desc.name).monospace(); - - let fp = match desc.style { - Style::Specific(ref style) => match style.to_lowercase().as_str() { - "italic" => fp.italic(), - "bold" => fp.bold(), - _ => fp, - }, - Style::Description { slant, weight } => { - let fp = match slant { - Slant::Normal => fp, - Slant::Italic => fp.italic(), - // This style is not supported by rust-font-loader - Slant::Oblique => return Err(Error::UnsupportedStyle), - }; - match weight { - Weight::Bold => fp.bold(), - Weight::Normal => fp, - } - }, - }; - self.fonts.push( - FontCollection::from_bytes( - system_fonts::get(&fp.build()).ok_or_else(|| Error::MissingFont(desc.clone()))?.0, - ) - .and_then(FontCollection::into_font) - .map_err(|_| Error::UnsupportedFont)?, - ); - Ok(FontKey { token: (self.fonts.len() - 1) as u16 }) - } - - fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> { - let scaled_glyph = self.fonts[glyph_key.font_key.token as usize] - .glyph(glyph_key.c) - .scaled(Scale::uniform(glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.)); - - let glyph = scaled_glyph.positioned(point(0.0, 0.0)); - - // Pixel bounding box - let bb = match glyph.pixel_bounding_box() { - Some(bb) => bb, - // Bounding box calculation fails for spaces so we provide a placeholder bounding box - None => rusttype::Rect { min: point(0, 0), max: point(0, 0) }, - }; - - let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize); - - glyph.draw(|_x, _y, v| { - buf.push((v * 255.0) as u8); - buf.push((v * 255.0) as u8); - buf.push((v * 255.0) as u8); - }); - Ok(RasterizedGlyph { - c: glyph_key.c, - width: bb.width(), - height: bb.height(), - top: -bb.min.y, - left: bb.min.x, - buf, - }) - } - - fn update_dpr(&mut self, device_pixel_ratio: f32) { - self.dpi_ratio = device_pixel_ratio; - } -} - -#[derive(Debug)] -pub enum Error { - MissingFont(FontDesc), - UnsupportedFont, - UnsupportedStyle, - // NOTE: This error is different from how the FreeType code handles it - MissingGlyph, -} - -impl ::std::error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::MissingFont(ref _desc) => "Couldn't find the requested font", - Error::UnsupportedFont => "Only TrueType fonts are supported", - Error::UnsupportedStyle => "The selected style is not supported by rusttype", - Error::MissingGlyph => "The selected font does not have the requested glyph", - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - Error::MissingFont(ref desc) => write!( - f, - "Couldn't find a font with {}\n\tPlease check the font config in your \ - alacritty.yml.", - desc - ), - Error::UnsupportedFont => write!( - f, - "Rusttype only supports TrueType fonts.\n\tPlease select a TrueType font instead." - ), - Error::UnsupportedStyle => { - write!(f, "The selected font style is not supported by rusttype.") - }, - Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."), - } - } -} |