blob: 8d8a3627f755f5f442b2d1cacc92fed7a1ed3c17 [file] [log] [blame]
// Copyright 2022 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Support for glyph rendering.
use crate::scene::Scene;
use peniko::kurbo::Affine;
use peniko::{Brush, Color, Fill, Style};
use skrifa::instance::{NormalizedCoord, Size};
use skrifa::outline::OutlinePen;
use skrifa::raw::FontRef;
use skrifa::setting::Setting;
use skrifa::{GlyphId, OutlineGlyphCollection};
use vello_encoding::Encoding;
use peniko::kurbo::Shape;
pub use skrifa;
use skrifa::outline::DrawSettings;
use skrifa::MetadataProvider;
pub use vello_encoding::Glyph;
/// General context for creating scene fragments for glyph outlines.
#[derive(Default)]
pub struct GlyphContext {
coords: Vec<NormalizedCoord>,
}
impl GlyphContext {
/// Creates a new context.
pub fn new() -> Self {
Self::default()
}
/// Creates a new provider for generating scene fragments for glyphs from
/// the specified font and settings.
pub fn new_provider<'a, V>(
&'a mut self,
font: &FontRef<'a>,
ppem: f32,
_hint: bool,
variations: V,
) -> GlyphProvider<'a>
where
V: IntoIterator,
V::Item: Into<Setting<f32>>,
{
let outlines = font.outline_glyphs();
let size = Size::new(ppem);
let axes = font.axes();
let axis_count = axes.len();
self.coords.clear();
self.coords.resize(axis_count, Default::default());
axes.location_to_slice(variations, &mut self.coords);
if self.coords.iter().all(|x| *x == NormalizedCoord::default()) {
self.coords.clear();
}
GlyphProvider {
outlines,
size,
coords: &self.coords,
}
}
}
/// Generator for scene fragments containing glyph outlines for a specific
/// font.
pub struct GlyphProvider<'a> {
outlines: OutlineGlyphCollection<'a>,
size: Size,
coords: &'a [NormalizedCoord],
}
impl<'a> GlyphProvider<'a> {
/// Returns a scene fragment containing the commands to render the
/// specified glyph.
pub fn get(&mut self, gid: u16, brush: Option<&Brush>) -> Option<Scene> {
let mut scene = Scene::new();
let mut path = BezPathPen::default();
let outline = self.outlines.get(GlyphId::new(gid))?;
let draw_settings = DrawSettings::unhinted(self.size, self.coords);
outline.draw(draw_settings, &mut path).ok()?;
scene.fill(
Fill::NonZero,
Affine::IDENTITY,
brush.unwrap_or(&Brush::Solid(Color::rgb8(255, 255, 255))),
None,
&path.0,
);
Some(scene)
}
pub fn encode_glyph(&mut self, gid: u16, style: &Style, encoding: &mut Encoding) -> Option<()> {
let fill = match style {
Style::Fill(fill) => *fill,
Style::Stroke(_) => Fill::NonZero,
};
encoding.encode_fill_style(fill);
let mut path = encoding.encode_path(true);
let outline = self.outlines.get(GlyphId::new(gid))?;
let draw_settings = DrawSettings::unhinted(self.size, self.coords);
match style {
Style::Fill(_) => {
outline.draw(draw_settings, &mut path).ok()?;
}
Style::Stroke(stroke) => {
const STROKE_TOLERANCE: f64 = 0.01;
let mut pen = BezPathPen::default();
outline.draw(draw_settings, &mut pen).ok()?;
let stroked = peniko::kurbo::stroke(
pen.0.path_elements(STROKE_TOLERANCE),
stroke,
&Default::default(),
STROKE_TOLERANCE,
);
path.shape(&stroked);
}
}
if path.finish(false) != 0 {
Some(())
} else {
None
}
}
}
#[derive(Default)]
struct BezPathPen(peniko::kurbo::BezPath);
impl OutlinePen for BezPathPen {
fn move_to(&mut self, x: f32, y: f32) {
self.0.move_to((x as f64, y as f64));
}
fn line_to(&mut self, x: f32, y: f32) {
self.0.line_to((x as f64, y as f64));
}
fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
self.0
.quad_to((cx0 as f64, cy0 as f64), (x as f64, y as f64));
}
fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
self.0.curve_to(
(cx0 as f64, cy0 as f64),
(cx1 as f64, cy1 as f64),
(x as f64, y as f64),
);
}
fn close(&mut self) {
self.0.close_path();
}
}