aboutsummaryrefslogtreecommitdiff
path: root/font/src/directwrite/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'font/src/directwrite/mod.rs')
-rw-r--r--font/src/directwrite/mod.rs209
1 files changed, 209 insertions, 0 deletions
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"),
+ }
+ }
+}