Skip to content

Commit

Permalink
fonts: detect colr glyphs better
Browse files Browse the repository at this point in the history
Freetype's support for colr is relatively rudimentary, which is fine,
but we want more control, so we probe a bit harder to figure out
whether a glyph has colr information before allowing freetype
to rasterize it, so that we have the opportunity to reroute
that rendering to our own logic.
  • Loading branch information
wez committed Aug 22, 2023
1 parent 7af6115 commit 67f4450
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 11 deletions.
43 changes: 35 additions & 8 deletions wezterm-font/src/ftwrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ pub struct SelectedFontSize {
#[error("Glyph is SVG")]
pub struct IsSvg;

#[derive(Debug, thiserror::Error)]
#[error("Glyph is COLR1 or later")]
pub struct IsColr1OrLater;

impl Face {
pub fn family_name(&self) -> String {
unsafe {
Expand Down Expand Up @@ -926,14 +930,16 @@ impl Face {
synthesize_bold: bool,
) -> anyhow::Result<&FT_GlyphSlotRec_> {
unsafe {
ft_result(FT_Load_Glyph(self.face, glyph_index, load_flags), ()).with_context(
|| {
format!(
"load_and_render_glyph: FT_Load_Glyph glyph_index:{}",
glyph_index
)
},
)?;
ft_result(
FT_Load_Glyph(self.face, glyph_index, load_flags | FT_LOAD_NO_SVG as i32),
(),
)
.with_context(|| {
format!(
"load_and_render_glyph: FT_Load_Glyph glyph_index:{}",
glyph_index
)
})?;
let slot = &mut *(*self.face).glyph;

if slot.format == FT_Glyph_Format_::FT_GLYPH_FORMAT_SVG {
Expand All @@ -944,8 +950,29 @@ impl Face {
FT_GlyphSlot_Embolden(slot as *mut _);
}

// Current versions of freetype overload the operation of FT_LOAD_COLOR
// and the resulting glyph format such that we cannot determine solely
// from the flags whether we got a regular set of outlines,
// or its COLR v0 synthesized glyphs, or whether it's COLR v1 or later
// and it can't render the result.
// So, we probe here to look for color layer information: if we find it,
// we don't call freetype's renderer and instead bubble up an error
// that the embedding application can trap and decide what to do.
if slot.format == FT_Glyph_Format_::FT_GLYPH_FORMAT_OUTLINE {
if self
.get_color_glyph_paint(
glyph_index,
FT_Color_Root_Transform::FT_COLOR_NO_ROOT_TRANSFORM,
)
.is_ok()
{
return Err(IsColr1OrLater.into());
}
}

ft_result(FT_Render_Glyph(slot, render_mode), ())
.context("load_and_render_glyph: FT_Render_Glyph")?;

Ok(slot)
}
}
Expand Down
8 changes: 5 additions & 3 deletions wezterm-font/src/rasterizer/freetype.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::ftwrap::{
composite_mode_to_operator, vector_x_y, FT_Affine23, FT_ColorIndex, FT_ColorLine, FT_ColorStop,
FT_Fixed, FT_Get_Colorline_Stops, FT_Int32, FT_PaintExtend, IsSvg, SelectedFontSize,
FT_LOAD_NO_HINTING,
FT_Fixed, FT_Get_Colorline_Stops, FT_Int32, FT_PaintExtend, IsColr1OrLater, IsSvg,
SelectedFontSize, FT_LOAD_NO_HINTING,
};
use crate::parser::ParsedFont;
use crate::rasterizer::colr::{
Expand Down Expand Up @@ -64,7 +64,9 @@ impl FontRasterizer for FreeTypeRasterizer {
) {
Ok(g) => g,
Err(err) => {
if err.root_cause().downcast_ref::<IsSvg>().is_some() {
if err.root_cause().downcast_ref::<IsSvg>().is_some()
|| err.root_cause().downcast_ref::<IsColr1OrLater>().is_some()
{
drop(face);

let config = config::configuration();
Expand Down

0 comments on commit 67f4450

Please sign in to comment.