vello/piet-scene/src/scene/builder.rs

454 lines
14 KiB
Rust
Raw Normal View History

2022-04-11 19:30:08 +10:00
// Copyright 2022 The piet-gpu 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 super::{conv, Scene, SceneData, SceneFragment};
use crate::ResourcePatch;
2022-04-11 19:30:08 +10:00
use bytemuck::{Pod, Zeroable};
use peniko::kurbo::{Affine, PathEl, Shape};
use peniko::{BlendMode, Brush, ColorStop, Fill, Stroke};
use smallvec::SmallVec;
2022-04-11 19:30:08 +10:00
/// Builder for constructing a scene or scene fragment.
pub struct SceneBuilder<'a> {
2022-04-11 19:30:08 +10:00
scene: &'a mut SceneData,
layers: SmallVec<[(BlendMode, bool); 8]>,
2022-04-11 19:30:08 +10:00
}
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)
}
2022-04-11 19:30:08 +10:00
/// Creates a new builder for constructing a scene.
fn new(scene: &'a mut SceneData, is_fragment: bool) -> Self {
scene.reset(is_fragment);
2022-04-11 20:14:58 +10:00
Self {
scene,
layers: Default::default(),
2022-04-11 20:14:58 +10:00
}
2022-04-11 19:30:08 +10:00
}
/// 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: BlendMode, transform: Affine, shape: &impl Shape) {
self.maybe_encode_transform(transform);
2022-04-11 20:05:40 +10:00
self.linewidth(-1.0);
if self.encode_path(shape, true) {
self.begin_clip(blend);
self.layers.push((blend, true));
} else {
// When the layer has an empty path, record an entry to prevent
// the stack from becoming unbalanced. This is handled in
// pop_layer.
self.layers.push((blend, false));
}
2022-04-11 19:30:08 +10:00
}
/// Pops the current layer.
2022-04-11 20:05:40 +10:00
pub fn pop_layer(&mut self) {
if let Some((blend, active)) = self.layers.pop() {
if active {
self.end_clip(blend);
}
2022-04-11 20:05:40 +10:00
}
}
2022-04-11 19:30:08 +10:00
/// Fills a shape using the specified style and brush.
pub fn fill(
2022-04-11 19:30:08 +10:00
&mut self,
_style: Fill,
transform: Affine,
2022-04-11 19:30:08 +10:00
brush: &Brush,
brush_transform: Option<Affine>,
shape: &impl Shape,
) {
self.maybe_encode_transform(transform);
2022-04-11 20:05:40 +10:00
self.linewidth(-1.0);
if self.encode_path(shape, true) {
if let Some(brush_transform) = brush_transform {
self.encode_transform(transform * brush_transform);
self.swap_last_tags();
self.encode_brush(brush);
} else {
self.encode_brush(brush);
}
2022-04-11 20:05:40 +10:00
}
2022-04-11 19:30:08 +10:00
}
/// Strokes a shape using the specified style and brush.
pub fn stroke(
2022-04-11 19:30:08 +10:00
&mut self,
style: &Stroke,
transform: Affine,
2022-04-11 19:30:08 +10:00
brush: &Brush,
brush_transform: Option<Affine>,
shape: &impl Shape,
) {
self.maybe_encode_transform(transform);
2022-04-11 20:05:40 +10:00
self.linewidth(style.width);
if self.encode_path(shape, false) {
if let Some(brush_transform) = brush_transform {
self.encode_transform(transform * brush_transform);
self.swap_last_tags();
self.encode_brush(brush);
} else {
self.encode_brush(brush);
}
2022-04-11 20:05:40 +10:00
}
2022-04-11 19:30:08 +10:00
}
/// Appends a fragment to the scene.
pub fn append(&mut self, fragment: &SceneFragment, transform: Option<Affine>) {
self.scene.append(&fragment.data, &transform);
2022-04-11 19:30:08 +10:00
}
/// Completes construction and finalizes the underlying scene.
2022-04-11 20:14:58 +10:00
pub fn finish(mut self) {
while !self.layers.is_empty() {
self.pop_layer();
2022-04-11 20:05:40 +10:00
}
}
}
2022-04-11 19:30:08 +10:00
impl<'a> SceneBuilder<'a> {
fn encode_path(&mut self, shape: &impl Shape, is_fill: bool) -> bool {
2022-11-08 15:08:10 +11:00
let mut b = PathBuilder::new(
&mut self.scene.tag_stream,
&mut self.scene.pathseg_stream,
is_fill,
);
for el in shape.path_elements(0.1) {
match el {
PathEl::MoveTo(p0) => b.move_to(p0.x as f32, p0.y as f32),
PathEl::LineTo(p0) => b.line_to(p0.x as f32, p0.y as f32),
PathEl::QuadTo(p0, p1) => {
b.quad_to(p0.x as f32, p0.y as f32, p1.x as f32, p1.y as f32)
}
PathEl::CurveTo(p0, p1, p2) => b.cubic_to(
p0.x as f32,
p0.y as f32,
p1.x as f32,
p1.y as f32,
p2.x as f32,
p2.y as f32,
),
PathEl::ClosePath => b.close_path(),
2022-04-11 19:30:08 +10:00
}
}
b.finish();
2022-11-08 15:08:10 +11:00
if b.n_pathseg != 0 {
self.scene.n_path += 1;
2022-11-08 15:08:10 +11:00
self.scene.n_pathseg += b.n_pathseg;
true
} else {
false
}
2022-04-11 19:30:08 +10:00
}
fn maybe_encode_transform(&mut self, transform: Affine) {
if self.scene.transform_stream.last() != Some(&conv::affine_to_f32(&transform)) {
self.encode_transform(transform);
}
}
fn encode_transform(&mut self, transform: Affine) {
2022-04-11 20:05:40 +10:00
self.scene.tag_stream.push(0x20);
self.scene
.transform_stream
.push(conv::affine_to_f32(&transform));
2022-04-11 20:05:40 +10:00
}
// Swap the last two tags in the tag stream; used for transformed
// gradients.
fn swap_last_tags(&mut self) {
let len = self.scene.tag_stream.len();
self.scene.tag_stream.swap(len - 1, len - 2);
}
// -1.0 means "fill"
fn linewidth(&mut self, linewidth: f32) {
if self.scene.linewidth_stream.last() != Some(&linewidth) {
self.scene.tag_stream.push(0x40);
self.scene.linewidth_stream.push(linewidth);
}
2022-04-11 20:05:40 +10:00
}
2022-04-11 19:30:08 +10:00
fn encode_brush(&mut self, brush: &Brush) {
match brush {
Brush::Solid(color) => {
self.scene.drawtag_stream.push(DRAWTAG_FILLCOLOR);
let rgba_color = color.to_premul_u32();
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&FillColor { rgba_color }));
}
Brush::LinearGradient(gradient) => {
let index = self.add_ramp(&gradient.stops);
self.scene.drawtag_stream.push(DRAWTAG_FILLLINGRADIENT);
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&FillLinGradient {
index,
p0: conv::point_to_f32(gradient.start),
p1: conv::point_to_f32(gradient.end),
2022-04-11 19:30:08 +10:00
}));
}
Brush::RadialGradient(gradient) => {
let index = self.add_ramp(&gradient.stops);
self.scene.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT);
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&FillRadGradient {
index,
p0: conv::point_to_f32(gradient.start_center),
p1: conv::point_to_f32(gradient.end_center),
r0: gradient.start_radius,
r1: gradient.end_radius,
2022-04-11 19:30:08 +10:00
}));
}
Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"),
}
}
fn add_ramp(&mut self, stops: &[ColorStop]) -> u32 {
let offset = self.scene.drawdata_stream.len();
let resources = &mut self.scene.resources;
let stops_start = resources.stops.len();
resources.stops.extend_from_slice(stops);
resources.patches.push(ResourcePatch::Ramp {
offset,
stops: stops_start..stops_start + stops.len(),
});
0
2022-04-11 19:30:08 +10:00
}
2022-04-11 20:05:40 +10:00
/// Start a clip.
fn begin_clip(&mut self, blend: BlendMode) {
2022-04-11 20:05:40 +10:00
self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP);
let element = Clip {
blend: encode_blend_mode(blend),
2022-04-11 20:05:40 +10:00
};
2022-04-11 20:14:58 +10:00
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&element));
2022-04-11 20:05:40 +10:00
self.scene.n_clip += 1;
}
fn end_clip(&mut self, blend: BlendMode) {
2022-04-11 20:05:40 +10:00
self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP);
let element = Clip {
blend: encode_blend_mode(blend),
2022-04-11 20:05:40 +10:00
};
2022-04-11 20:14:58 +10:00
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&element));
2022-04-11 20:05:40 +10:00
// This is a dummy path, and will go away with the new clip impl.
self.scene.tag_stream.push(0x10);
self.scene.n_path += 1;
self.scene.n_clip += 1;
}
2022-04-11 19:30:08 +10:00
}
2022-04-11 20:05:40 +10:00
fn encode_blend_mode(mode: BlendMode) -> u32 {
(mode.mix as u32) << 8 | mode.compose as u32
}
2022-04-11 19:30:08 +10:00
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
const DRAWTAG_FILLCOLOR: u32 = 0x44;
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;
const DRAWTAG_FILLRADGRADIENT: u32 = 0x2dc;
const DRAWTAG_BEGINCLIP: u32 = 0x05;
const DRAWTAG_ENDCLIP: u32 = 0x25;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillColor {
rgba_color: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillLinGradient {
index: u32,
p0: [f32; 2],
p1: [f32; 2],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillRadGradient {
index: u32,
p0: [f32; 2],
p1: [f32; 2],
r0: f32,
r1: f32,
}
#[allow(unused)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillImage {
index: u32,
// [i16; 2]
offset: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct Clip {
blend: u32,
}
struct PathBuilder<'a> {
tag_stream: &'a mut Vec<u8>,
// If we're never going to use the i16 encoding, it might be
// slightly faster to store this as Vec<u32>, we'd get aligned
// stores on ARM etc.
pathseg_stream: &'a mut Vec<u8>,
first_pt: [f32; 2],
state: PathState,
n_pathseg: u32,
2022-11-08 15:08:10 +11:00
is_fill: bool,
2022-04-11 19:30:08 +10:00
}
#[derive(PartialEq)]
enum PathState {
Start,
MoveTo,
NonemptySubpath,
}
impl<'a> PathBuilder<'a> {
2022-11-08 15:08:10 +11:00
pub fn new(tags: &'a mut Vec<u8>, pathsegs: &'a mut Vec<u8>, is_fill: bool) -> PathBuilder<'a> {
2022-04-11 19:30:08 +10:00
PathBuilder {
tag_stream: tags,
pathseg_stream: pathsegs,
first_pt: [0.0, 0.0],
state: PathState::Start,
n_pathseg: 0,
2022-11-08 15:08:10 +11:00
is_fill,
2022-04-11 19:30:08 +10:00
}
}
pub fn move_to(&mut self, x: f32, y: f32) {
2022-11-08 15:08:10 +11:00
if self.is_fill {
self.close_path();
}
2022-04-11 19:30:08 +10:00
let buf = [x, y];
let bytes = bytemuck::bytes_of(&buf);
self.first_pt = buf;
if self.state == PathState::MoveTo {
let new_len = self.pathseg_stream.len() - 8;
self.pathseg_stream.truncate(new_len);
2022-11-08 15:08:10 +11:00
} else if self.state == PathState::NonemptySubpath {
2022-04-11 19:30:08 +10:00
if let Some(tag) = self.tag_stream.last_mut() {
*tag |= 4;
}
}
self.pathseg_stream.extend_from_slice(bytes);
self.state = PathState::MoveTo;
}
pub fn line_to(&mut self, x: f32, y: f32) {
if self.state == PathState::Start {
2022-11-08 15:08:10 +11:00
self.move_to(self.first_pt[0], self.first_pt[1]);
2022-04-11 19:30:08 +10:00
}
let buf = [x, y];
let bytes = bytemuck::bytes_of(&buf);
self.pathseg_stream.extend_from_slice(bytes);
self.tag_stream.push(9);
self.state = PathState::NonemptySubpath;
self.n_pathseg += 1;
}
pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
if self.state == PathState::Start {
2022-11-08 15:08:10 +11:00
self.move_to(self.first_pt[0], self.first_pt[1]);
2022-04-11 19:30:08 +10:00
}
let buf = [x1, y1, x2, y2];
let bytes = bytemuck::bytes_of(&buf);
self.pathseg_stream.extend_from_slice(bytes);
self.tag_stream.push(10);
self.state = PathState::NonemptySubpath;
self.n_pathseg += 1;
}
pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) {
if self.state == PathState::Start {
2022-11-08 15:08:10 +11:00
self.move_to(self.first_pt[0], self.first_pt[1]);
2022-04-11 19:30:08 +10:00
}
let buf = [x1, y1, x2, y2, x3, y3];
let bytes = bytemuck::bytes_of(&buf);
self.pathseg_stream.extend_from_slice(bytes);
self.tag_stream.push(11);
self.state = PathState::NonemptySubpath;
self.n_pathseg += 1;
}
pub fn close_path(&mut self) {
match self.state {
PathState::Start => return,
PathState::MoveTo => {
let new_len = self.pathseg_stream.len() - 8;
self.pathseg_stream.truncate(new_len);
self.state = PathState::Start;
return;
}
PathState::NonemptySubpath => (),
}
let len = self.pathseg_stream.len();
if len < 8 {
// can't happen
return;
}
let first_bytes = bytemuck::bytes_of(&self.first_pt);
if &self.pathseg_stream[len - 8..len] != first_bytes {
self.pathseg_stream.extend_from_slice(first_bytes);
self.tag_stream.push(13);
self.n_pathseg += 1;
} else {
if let Some(tag) = self.tag_stream.last_mut() {
*tag |= 4;
}
}
self.state = PathState::Start;
}
pub fn finish(&mut self) {
2022-11-08 15:08:10 +11:00
if self.is_fill {
self.close_path();
}
2022-04-11 19:30:08 +10:00
if self.state == PathState::MoveTo {
let new_len = self.pathseg_stream.len() - 8;
self.pathseg_stream.truncate(new_len);
}
if self.n_pathseg != 0 {
if let Some(tag) = self.tag_stream.last_mut() {
*tag |= 4;
}
self.tag_stream.push(0x10);
2022-04-11 19:30:08 +10:00
}
}
}