Skip to content

Commit

Permalink
Replace fello with skrifa for font rendering (#423)
Browse files Browse the repository at this point in the history
This replaces our bespoke fello lib with skrifa from crates.io which brings us one step closer to being able to release.

Causes a slight regression since we no longer support hinting but that will be fixed when support lands upstream (soon!)
  • Loading branch information
dfrg authored Jan 25, 2024
1 parent e04b602 commit 1c06c30
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 91 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ buffer_labels = []

[dependencies]
bytemuck = { workspace = true }
fello = { workspace = true }
skrifa = { workspace = true }
peniko = { workspace = true }
wgpu = { workspace = true, optional = true }
raw-window-handle = "0.5"
Expand All @@ -57,7 +57,7 @@ kurbo = "0.10.4"

[workspace.dependencies]
bytemuck = { version = "1.12.1", features = ["derive"] }
fello = { git = "https://github.com/dfrg/fount", rev = "dadbcf75695f035ca46766bfd60555d05bd421b1" }
skrifa = "0.15.4"
peniko = { git = "https://github.com/linebender/peniko", rev = "629fc3325b016a8c98b1cd6204cb4ddf1c6b3daa" }

# NOTE: Make sure to keep this in sync with the version badge in README.md
Expand Down
4 changes: 2 additions & 2 deletions crates/encoding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ repository.workspace = true
default = ["full"]
# Enables support for the full pipeline including late-bound
# resources (gradients, images and glyph runs)
full = ["fello", "guillotiere"]
full = ["skrifa", "guillotiere"]

[dependencies]
bytemuck = { workspace = true }
fello = { workspace = true, optional = true }
skrifa = { workspace = true, optional = true }
peniko = { workspace = true }
guillotiere = { version = "0.6.2", optional = true }
2 changes: 1 addition & 1 deletion crates/encoding/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use peniko::{
#[cfg(feature = "full")]
use {
super::{DrawImage, DrawLinearGradient, DrawRadialGradient, Glyph, GlyphRun, Patch},
fello::NormalizedCoord,
peniko::{ColorStop, Extend, GradientKind, Image},
skrifa::instance::NormalizedCoord,
};

/// Encoded data streams for a scene.
Expand Down
29 changes: 15 additions & 14 deletions crates/encoding/src/glyph_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ use std::collections::HashMap;

use super::{Encoding, StreamOffsets};

use fello::scale::{Pen, Scaler};
use fello::GlyphId;
use peniko::{
kurbo::{BezPath, Shape},
Fill, Style,
};
use skrifa::{instance::NormalizedCoord, outline::OutlinePen, GlyphId, OutlineGlyphCollection};

#[derive(Copy, Clone, PartialEq, Eq, Hash, Default, Debug)]
pub struct GlyphKey {
pub font_id: u64,
pub font_index: u32,
pub glyph_id: u32,
pub font_size: u32,
pub font_size_bits: u32,
pub hint: bool,
}

Expand All @@ -35,11 +34,14 @@ impl GlyphCache {

pub fn get_or_insert(
&mut self,
outlines: &OutlineGlyphCollection,
key: GlyphKey,
style: &Style,
scaler: &mut Scaler,
font_size: f32,
coords: &[NormalizedCoord],
) -> Option<CachedRange> {
let is_var = !scaler.normalized_coords().is_empty();
let size = skrifa::instance::Size::new(font_size);
let is_var = !coords.is_empty();
let encoding_cache = &mut self.encoding;
let mut encode_glyph = || {
let start = encoding_cache.stream_offsets();
Expand All @@ -49,18 +51,18 @@ impl GlyphCache {
};
encoding_cache.encode_fill_style(fill);
let mut path = encoding_cache.encode_path(true);
let outline = outlines.get(GlyphId::new(key.glyph_id as u16))?;
// FIXME: Re-add hinting when skrifa supports it
// Tracking issue <https://github.com/googlefonts/fontations/issues/620>
let draw_settings = skrifa::outline::DrawSettings::unhinted(size, coords);
match style {
Style::Fill(_) => {
scaler
.outline(GlyphId::new(key.glyph_id as u16), &mut path)
.ok()?;
outline.draw(draw_settings, &mut path).ok()?;
}
Style::Stroke(stroke) => {
const STROKE_TOLERANCE: f64 = 0.01;
let mut pen = BezPathPen::default();
scaler
.outline(GlyphId::new(key.glyph_id as u16), &mut pen)
.ok()?;
outline.draw(draw_settings, &mut pen).ok()?;
let stroked = peniko::kurbo::stroke(
pen.0.path_elements(STROKE_TOLERANCE),
stroke,
Expand Down Expand Up @@ -110,12 +112,11 @@ impl CachedRange {
}
}

// A wrapper newtype so we can implement the `Pen` trait. Arguably, this could
// be in the fello crate, but will go away when we do GPU-side stroking.
// A wrapper newtype so we can implement the `OutlinePen` trait.
#[derive(Default)]
struct BezPathPen(BezPath);

impl Pen for BezPathPen {
impl OutlinePen for BezPathPen {
fn move_to(&mut self, x: f32, y: f32) {
self.0.move_to((x as f64, y as f64));
}
Expand Down
2 changes: 1 addition & 1 deletion crates/encoding/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ impl<'a> PathEncoder<'a> {
}

#[cfg(feature = "full")]
impl fello::scale::Pen for PathEncoder<'_> {
impl skrifa::outline::OutlinePen for PathEncoder<'_> {
fn move_to(&mut self, x: f32, y: f32) {
self.move_to(x, y);
}
Expand Down
28 changes: 8 additions & 20 deletions crates/encoding/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use bytemuck::{Pod, Zeroable};
use skrifa::MetadataProvider;

use super::{DrawTag, Encoding, PathTag, StreamOffsets, Style, Transform};

Expand Down Expand Up @@ -164,7 +165,6 @@ pub fn resolve_solid_paths_only(encoding: &Encoding, packed: &mut Vec<u8>) -> La
pub struct Resolver {
glyph_cache: GlyphCache,
glyph_ranges: Vec<CachedRange>,
glyph_cx: fello::scale::Context,
ramp_cache: RampCache,
image_cache: ImageCache,
pending_images: Vec<PendingImage>,
Expand Down Expand Up @@ -414,23 +414,18 @@ impl Resolver {
let mut run_sizes = StreamOffsets::default();
let run = &resources.glyph_runs[*index];
let font_id = run.font.data.id();
let font_size_u32 = run.font_size.to_bits();
let Ok(font_file) = fello::raw::FileRef::new(run.font.data.as_ref()) else {
let Ok(font_file) = skrifa::raw::FileRef::new(run.font.data.as_ref()) else {
continue;
};
let font = match font_file {
fello::raw::FileRef::Font(font) => Some(font),
fello::raw::FileRef::Collection(collection) => {
skrifa::raw::FileRef::Font(font) => Some(font),
skrifa::raw::FileRef::Collection(collection) => {
collection.get(run.font.index).ok()
}
};
let Some(font) = font else { continue };
let glyphs = &resources.glyphs[run.glyphs.clone()];
let coords = &resources.normalized_coords[run.normalized_coords.clone()];
let key = fello::FontKey {
data_id: font_id,
index: run.font.index,
};
let mut hint = run.hint;
let mut font_size = run.font_size;
let mut transform = run.transform;
Expand All @@ -448,26 +443,19 @@ impl Resolver {
hint = false;
}
}
let mut scaler = self
.glyph_cx
.new_scaler()
.key(Some(key))
.hint(hint.then_some(fello::scale::Hinting::VerticalSubpixel))
.coords(coords)
.size(fello::Size::new(font_size))
.build(&font);
let outlines = font.outline_glyphs();
let glyph_start = self.glyph_ranges.len();
for glyph in glyphs {
let key = GlyphKey {
font_id,
font_index: run.font.index,
font_size: font_size_u32,
font_size_bits: font_size.to_bits(),
glyph_id: glyph.id,
hint: run.hint,
hint,
};
let encoding_range = self
.glyph_cache
.get_or_insert(key, &run.style, &mut scaler)
.get_or_insert(&outlines, key, &run.style, font_size, coords)
.unwrap_or_default();
run_sizes.add(&encoding_range.len());
self.glyph_ranges.push(encoding_range);
Expand Down
29 changes: 13 additions & 16 deletions examples/scenes/src/simple_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
use std::sync::Arc;

use vello::{
fello::meta::MetadataProvider,
fello::raw::FontRef,
glyph::{Glyph, GlyphContext},
kurbo::Affine,
peniko::{Blob, Brush, BrushRef, Font, StyleRef},
skrifa::{raw::FontRef, MetadataProvider},
SceneBuilder,
};

Expand All @@ -31,7 +30,6 @@ const ROBOTO_FONT: &[u8] = include_bytes!("../../assets/roboto/Roboto-Regular.tt
const INCONSOLATA_FONT: &[u8] = include_bytes!("../../assets/inconsolata/Inconsolata.ttf");

pub struct SimpleText {
gcx: GlyphContext,
roboto: Font,
inconsolata: Font,
}
Expand All @@ -40,7 +38,6 @@ impl SimpleText {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
gcx: GlyphContext::new(),
roboto: Font::new(Blob::new(Arc::new(ROBOTO_FONT)), 0),
inconsolata: Font::new(Blob::new(Arc::new(INCONSOLATA_FONT)), 0),
}
Expand Down Expand Up @@ -94,22 +91,20 @@ impl SimpleText {
let brush = brush.into();
let style = style.into();
let axes = font_ref.axes();
let fello_size = vello::fello::Size::new(size);
let coords = axes
.normalize(variations.iter().copied())
.collect::<Vec<_>>();
let font_size = vello::skrifa::instance::Size::new(size);
let var_loc = axes.location(variations.iter().copied());
let charmap = font_ref.charmap();
let metrics = font_ref.metrics(fello_size, coords.as_slice().into());
let metrics = font_ref.metrics(font_size, &var_loc);
let line_height = metrics.ascent - metrics.descent + metrics.leading;
let glyph_metrics = font_ref.glyph_metrics(fello_size, coords.as_slice().into());
let glyph_metrics = font_ref.glyph_metrics(font_size, &var_loc);
let mut pen_x = 0f32;
let mut pen_y = 0f32;
builder
.draw_glyphs(font)
.font_size(size)
.transform(transform)
.glyph_transform(glyph_transform)
.normalized_coords(&coords)
.normalized_coords(var_loc.coords())
.brush(brush)
.draw(
style,
Expand Down Expand Up @@ -143,15 +138,17 @@ impl SimpleText {
) {
let default_font = FontRef::new(ROBOTO_FONT).unwrap();
let font = font.and_then(to_font_ref).unwrap_or(default_font);
let fello_size = vello::fello::Size::new(size);
let font_size = vello::skrifa::instance::Size::new(size);
let var_loc = vello::skrifa::instance::LocationRef::default();
let charmap = font.charmap();
let metrics = font.metrics(fello_size, Default::default());
let metrics = font.metrics(font_size, var_loc);
let line_height = metrics.ascent - metrics.descent + metrics.leading;
let glyph_metrics = font.glyph_metrics(fello_size, Default::default());
let glyph_metrics = font.glyph_metrics(font_size, var_loc);
let mut pen_x = 0f64;
let mut pen_y = 0f64;
let vars: [(&str, f32); 0] = [];
let mut provider = self.gcx.new_provider(&font, None, size, false, vars);
let mut gcx = GlyphContext::new();
let mut provider = gcx.new_provider(&font, size, false, &vars);
for ch in text.chars() {
if ch == '\n' {
pen_y += line_height as f64;
Expand All @@ -172,7 +169,7 @@ impl SimpleText {
}

fn to_font_ref(font: &Font) -> Option<FontRef<'_>> {
use vello::fello::raw::FileRef;
use vello::skrifa::raw::FileRef;
let file_ref = FileRef::new(font.data.as_ref()).ok()?;
match file_ref {
FileRef::Font(font) => Some(font),
Expand Down
Loading

0 comments on commit 1c06c30

Please sign in to comment.