blob: 84e9ef30dadf543a7a7ba2648b83c4e9193dfb99 [file] [log] [blame]
// Copyright 2022 The vello authors.
//
// 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
//
// https://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.
//
// Also licensed under MIT license, at your choice.
//! Support for glyph rendering.
use crate::scene::{SceneBuilder, SceneFragment};
use {
fello::{
raw::types::GlyphId,
raw::FontRef,
scale::{Context, Pen, Scaler},
FontKey, Setting, Size,
},
peniko::kurbo::Affine,
peniko::{Brush, Color, Fill, Style},
vello_encoding::Encoding,
};
pub use fello;
use peniko::kurbo::Shape;
pub use vello_encoding::Glyph;
/// General context for creating scene fragments for glyph outlines.
pub struct GlyphContext {
ctx: Context,
}
impl Default for GlyphContext {
fn default() -> Self {
Self::new()
}
}
impl GlyphContext {
/// Creates a new context.
pub fn new() -> Self {
Self {
ctx: Context::new(),
}
}
/// 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>,
font_id: Option<FontKey>,
ppem: f32,
hint: bool,
variations: V,
) -> GlyphProvider<'a>
where
V: IntoIterator,
V::Item: Into<Setting<f32>>,
{
let scaler = self
.ctx
.new_scaler()
.size(Size::new(ppem))
.hint(hint.then_some(fello::scale::Hinting::VerticalSubpixel))
.key(font_id)
.variations(variations)
.build(font);
GlyphProvider { scaler }
}
}
/// Generator for scene fragments containing glyph outlines for a specific
/// font.
pub struct GlyphProvider<'a> {
scaler: Scaler<'a>,
}
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<SceneFragment> {
let mut fragment = SceneFragment::default();
let mut builder = SceneBuilder::for_fragment(&mut fragment);
let mut path = BezPathPen::default();
self.scaler.outline(GlyphId::new(gid), &mut path).ok()?;
builder.fill(
Fill::NonZero,
Affine::IDENTITY,
brush.unwrap_or(&Brush::Solid(Color::rgb8(255, 255, 255))),
None,
&path.0,
);
Some(fragment)
}
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);
match style {
Style::Fill(_) => {
self.scaler.outline(GlyphId::new(gid), &mut path).ok()?;
}
Style::Stroke(stroke) => {
const STROKE_TOLERANCE: f64 = 0.01;
let mut pen = BezPathPen::default();
self.scaler.outline(GlyphId::new(gid), &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 Pen 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()
}
}