blob: 13c61ca1aec472b899a56f8b6bfa693c3354bae0 [file] [log] [blame] [edit]
// 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.
use fello::NormalizedCoord;
use peniko::kurbo::{Affine, Rect, Shape, Stroke};
use peniko::{BlendMode, BrushRef, Color, Fill, Font, Image, StyleRef};
use vello_encoding::{Encoding, Glyph, GlyphRun, Patch, Transform};
/// Encoded definition of a scene and associated resources.
#[derive(Default)]
pub struct Scene {
data: Encoding,
}
impl Scene {
/// Creates a new scene.
pub fn new() -> Self {
Self::default()
}
/// Returns the raw encoded scene data streams.
pub fn data(&self) -> &Encoding {
&self.data
}
}
/// Encoded definition of a scene fragment and associated resources.
#[derive(Default)]
pub struct SceneFragment {
data: Encoding,
}
impl SceneFragment {
/// Creates a new scene fragment.
pub fn new() -> Self {
Self::default()
}
/// Returns true if the fragment does not contain any paths.
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
/// Returns the the entire sequence of points in the scene fragment.
pub fn points(&self) -> &[[f32; 2]] {
if self.is_empty() {
&[]
} else {
bytemuck::cast_slice(&self.data.path_data)
}
}
}
/// Builder for constructing a scene or scene fragment.
pub struct SceneBuilder<'a> {
scene: &'a mut Encoding,
}
impl<'a> SceneBuilder<'a> {
/// Creates a new builder for filling a scene. Any current content in the scene
/// will be cleared.
pub fn for_scene(scene: &'a mut Scene) -> Self {
Self::new(&mut scene.data, false)
}
/// Creates a new builder for filling a scene fragment. Any current content in
/// the fragment will be cleared.
pub fn for_fragment(fragment: &'a mut SceneFragment) -> Self {
Self::new(&mut fragment.data, true)
}
/// Creates a new builder for constructing a scene.
fn new(scene: &'a mut Encoding, is_fragment: bool) -> Self {
scene.reset(is_fragment);
Self { scene }
}
/// Pushes a new layer bound by the specifed shape and composed with
/// previous layers using the specified blend mode.
pub fn push_layer(
&mut self,
blend: impl Into<BlendMode>,
alpha: f32,
transform: Affine,
shape: &impl Shape,
) {
let blend = blend.into();
self.scene
.encode_transform(Transform::from_kurbo(&transform));
self.scene.encode_fill_style(Fill::NonZero);
if !self.scene.encode_shape(shape, true) {
// If the layer shape is invalid, encode a valid empty path. This suppresses
// all drawing until the layer is popped.
self.scene
.encode_shape(&Rect::new(0.0, 0.0, 0.0, 0.0), true);
}
self.scene.encode_begin_clip(blend, alpha.clamp(0.0, 1.0));
}
/// Pops the current layer.
pub fn pop_layer(&mut self) {
self.scene.encode_end_clip();
}
/// Fills a shape using the specified style and brush.
pub fn fill<'b>(
&mut self,
style: Fill,
transform: Affine,
brush: impl Into<BrushRef<'b>>,
brush_transform: Option<Affine>,
shape: &impl Shape,
) {
self.scene
.encode_transform(Transform::from_kurbo(&transform));
self.scene.encode_fill_style(style);
if self.scene.encode_shape(shape, true) {
if let Some(brush_transform) = brush_transform {
if self
.scene
.encode_transform(Transform::from_kurbo(&(transform * brush_transform)))
{
self.scene.swap_last_path_tags();
}
}
self.scene.encode_brush(brush, 1.0);
}
}
/// Strokes a shape using the specified style and brush.
pub fn stroke<'b>(
&mut self,
style: &Stroke,
transform: Affine,
brush: impl Into<BrushRef<'b>>,
brush_transform: Option<Affine>,
shape: &impl Shape,
) {
// The setting for tolerance are a compromise. For most applications,
// shape tolerance doesn't matter, as the input is likely Bézier paths,
// which is exact. Note that shape tolerance is hard-coded as 0.1 in
// the encoding crate.
//
// Stroke tolerance is a different matter. Generally, the cost scales
// with inverse O(n^6), so there is moderate rendering cost to setting
// too fine a value. On the other hand, error scales with the transform
// applied post-stroking, so may exceed visible threshold. When we do
// GPU-side stroking, the transform will be known. In the meantime,
// this is a compromise.
const SHAPE_TOLERANCE: f64 = 0.01;
const STROKE_TOLERANCE: f64 = SHAPE_TOLERANCE;
let stroked = peniko::kurbo::stroke(
shape.path_elements(SHAPE_TOLERANCE),
style,
&Default::default(),
STROKE_TOLERANCE,
);
self.fill(Fill::NonZero, transform, brush, brush_transform, &stroked);
}
/// Draws an image at its natural size with the given transform.
pub fn draw_image(&mut self, image: &Image, transform: Affine) {
self.fill(
Fill::NonZero,
transform,
image,
None,
&Rect::new(0.0, 0.0, image.width as f64, image.height as f64),
);
}
/// Returns a builder for encoding a glyph run.
pub fn draw_glyphs(&mut self, font: &Font) -> DrawGlyphs {
DrawGlyphs::new(self.scene, font)
}
/// Appends a fragment to the scene.
pub fn append(&mut self, fragment: &SceneFragment, transform: Option<Affine>) {
self.scene.append(
&fragment.data,
&transform.map(|xform| Transform::from_kurbo(&xform)),
);
}
}
/// Builder for encoding a glyph run.
pub struct DrawGlyphs<'a> {
encoding: &'a mut Encoding,
run: GlyphRun,
brush: BrushRef<'a>,
brush_alpha: f32,
}
impl<'a> DrawGlyphs<'a> {
/// Creates a new builder for encoding a glyph run for the specified
/// encoding with the given font.
pub fn new(encoding: &'a mut Encoding, font: &Font) -> Self {
let coords_start = encoding.resources.normalized_coords.len();
let glyphs_start = encoding.resources.glyphs.len();
let stream_offsets = encoding.stream_offsets();
Self {
encoding,
run: GlyphRun {
font: font.clone(),
transform: Transform::IDENTITY,
glyph_transform: None,
font_size: 16.0,
hint: false,
normalized_coords: coords_start..coords_start,
style: Fill::NonZero.into(),
glyphs: glyphs_start..glyphs_start,
stream_offsets,
},
brush: Color::BLACK.into(),
brush_alpha: 1.0,
}
}
/// Sets the global transform. This is applied to all glyphs after the offset
/// translation.
///
/// The default value is the identity matrix.
pub fn transform(mut self, transform: Affine) -> Self {
self.run.transform = Transform::from_kurbo(&transform);
self
}
/// Sets the per-glyph transform. This is applied to all glyphs prior to
/// offset translation. This is common used for applying a shear to simulate
/// an oblique font.
///
/// The default value is `None`.
pub fn glyph_transform(mut self, transform: Option<Affine>) -> Self {
self.run.glyph_transform = transform.map(|xform| Transform::from_kurbo(&xform));
self
}
/// Sets the font size in pixels per em units.
///
/// The default value is 16.0.
pub fn font_size(mut self, size: f32) -> Self {
self.run.font_size = size;
self
}
/// Sets whether to enable hinting.
///
/// The default value is `false`.
pub fn hint(mut self, hint: bool) -> Self {
self.run.hint = hint;
self
}
/// Sets the normalized design space coordinates for a variable font instance.
pub fn normalized_coords(mut self, coords: &[NormalizedCoord]) -> Self {
self.encoding
.resources
.normalized_coords
.truncate(self.run.normalized_coords.start);
self.encoding
.resources
.normalized_coords
.extend_from_slice(coords);
self.run.normalized_coords.end = self.encoding.resources.normalized_coords.len();
self
}
/// Sets the brush.
///
/// The default value is solid black.
pub fn brush(mut self, brush: impl Into<BrushRef<'a>>) -> Self {
self.brush = brush.into();
self
}
/// Sets an additional alpha multiplier for the brush.
///
/// The default value is 1.0.
pub fn brush_alpha(mut self, alpha: f32) -> Self {
self.brush_alpha = alpha;
self
}
/// Encodes a fill or stroke for for the given sequence of glyphs and consumes
/// the builder.
///
/// The `style` parameter accepts either `Fill` or `&Stroke` types.
pub fn draw(mut self, style: impl Into<StyleRef<'a>>, glyphs: impl Iterator<Item = Glyph>) {
let resources = &mut self.encoding.resources;
self.run.style = style.into().to_owned();
resources.glyphs.extend(glyphs);
self.run.glyphs.end = resources.glyphs.len();
if self.run.glyphs.is_empty() {
resources
.normalized_coords
.truncate(self.run.normalized_coords.start);
return;
}
let index = resources.glyph_runs.len();
resources.glyph_runs.push(self.run);
resources.patches.push(Patch::GlyphRun { index });
self.encoding.encode_brush(self.brush, self.brush_alpha);
}
}