blob: b398e6f8c99f57ffc3ba7c85ed12c46c99c00d6d [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 fello::scale::Pen;
use crate::encoding::{Encoding, PathEncoder};
use crate::scene::{SceneBuilder, SceneFragment};
use peniko::kurbo::Affine;
use peniko::{Brush, Color, Fill, Style};
use fello::{
raw::types::GlyphId,
raw::FontRef,
scale::{Context, Scaler},
FontKey, Setting, Size,
};
/// 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<()> {
match style {
Style::Fill(Fill::NonZero) => encoding.encode_linewidth(-1.0),
Style::Fill(Fill::EvenOdd) => encoding.encode_linewidth(-2.0),
Style::Stroke(stroke) => encoding.encode_linewidth(stroke.width),
}
let mut path = PathEncoderPen(encoding.encode_path(matches!(style, Style::Fill(_))));
self.scaler.outline(GlyphId::new(gid), &mut path).ok()?;
if path.0.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()
}
}
pub(crate) struct PathEncoderPen<'a>(pub PathEncoder<'a>);
impl Pen for PathEncoderPen<'_> {
fn move_to(&mut self, x: f32, y: f32) {
self.0.move_to(x, y)
}
fn line_to(&mut self, x: f32, y: f32) {
self.0.line_to(x, y)
}
fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
self.0.quad_to(cx0, cy0, x, y)
}
fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
self.0.cubic_to(cx0, cy0, cx1, cy1, x, y)
}
fn close(&mut self) {
self.0.close()
}
}