Add layer encoding

This commit is contained in:
Chad Brokaw 2022-04-11 06:05:40 -04:00
parent 753b97c342
commit f8f91e4207
4 changed files with 134 additions and 8 deletions

View file

@ -115,6 +115,26 @@ impl Affine {
y: point.y * self.yy + point.y * self.xy + self.dy, y: point.y * self.yy + point.y * self.xy + self.dy,
} }
} }
/// Compute the determinant of this transform.
pub fn determinant(self) -> f32 {
self.xx * self.yy - self.yx * self.xy
}
/// Compute the inverse transform.
///
/// Produces NaN values when the determinant is zero.
pub fn inverse(self) -> Self {
let inv_det = self.determinant().recip();
Self::new(&[
inv_det * self.yy,
-inv_det * self.yx,
-inv_det * self.xy,
inv_det * self.xx,
inv_det * (self.xy * self.dy - self.yy * self.dx),
inv_det * (self.yx * self.dx - self.xx * self.dy),
])
}
} }
impl std::ops::Mul for Affine { impl std::ops::Mul for Affine {

View file

@ -1,12 +1,14 @@
mod ramp_cache; mod gradient;
use crate::brush::{Brush, Stop}; use crate::brush::{Brush, Stop};
use ramp_cache::RampCache; use gradient::RampCache;
use std::collections::HashMap;
/// Context for caching resources across rendering operations. /// Context for caching resources across rendering operations.
#[derive(Default)] #[derive(Default)]
pub struct ResourceContext { pub struct ResourceContext {
ramp_cache: RampCache, ramps: RampCache,
persistent_map: HashMap<u64, PersistentBrushData>,
} }
impl ResourceContext { impl ResourceContext {
@ -15,14 +17,23 @@ impl ResourceContext {
} }
pub fn advance(&mut self) { pub fn advance(&mut self) {
self.ramp_cache.advance(); self.ramps.advance();
}
pub fn clear(&mut self) {
self.ramps.clear();
self.persistent_map.clear();
} }
pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 { pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 {
self.ramp_cache.add(stops) self.ramps.add(stops)
} }
pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush { pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush {
match brush {
Brush::Persistent(dup) => return *dup,
_ => {}
}
PersistentBrush { kind: 0, id: 0 } PersistentBrush { kind: 0, id: 0 }
} }
@ -35,3 +46,8 @@ pub struct PersistentBrush {
kind: u8, kind: u8,
id: u64, id: u64,
} }
struct PersistentBrushData {
brush: Brush,
}

View file

@ -21,6 +21,8 @@ use crate::resource::ResourceContext;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use core::borrow::Borrow; use core::borrow::Borrow;
const MAX_BLEND_STACK: usize = 256;
/// Creates a new builder for constructing a scene. /// Creates a new builder for constructing a scene.
pub fn build_scene<'a>(scene: &'a mut Scene, resources: &'a mut ResourceContext) -> Builder<'a> { pub fn build_scene<'a>(scene: &'a mut Scene, resources: &'a mut ResourceContext) -> Builder<'a> {
Builder::new(&mut scene.data, ResourceData::Scene(resources)) Builder::new(&mut scene.data, ResourceData::Scene(resources))
@ -38,12 +40,15 @@ pub fn build_fragment<'a>(fragment: &'a mut Fragment) -> Builder<'a> {
pub struct Builder<'a> { pub struct Builder<'a> {
scene: &'a mut SceneData, scene: &'a mut SceneData,
resources: ResourceData<'a>, resources: ResourceData<'a>,
layers: Vec<Blend>,
} }
impl<'a> Builder<'a> { impl<'a> Builder<'a> {
/// Creates a new builder for constructing a scene. /// Creates a new builder for constructing a scene.
fn new(scene: &'a mut SceneData, resources: ResourceData<'a>) -> Self { fn new(scene: &'a mut SceneData, resources: ResourceData<'a>) -> Self {
Self { scene, resources } scene.clear();
resources.clear();
Self { scene, resources, layers: vec![] }
} }
/// Pushes a transform matrix onto the stack. /// Pushes a transform matrix onto the stack.
@ -60,12 +65,22 @@ impl<'a> Builder<'a> {
E::IntoIter: Clone, E::IntoIter: Clone,
E::Item: Borrow<Element>, E::Item: Borrow<Element>,
{ {
self.linewidth(-1.0);
let elements = elements.into_iter(); let elements = elements.into_iter();
self.encode_path(elements, true); self.encode_path(elements, true);
self.begin_clip(Some(blend));
if self.layers.len() >= MAX_BLEND_STACK {
panic!("Maximum clip/blend stack size {} exceeded", MAX_BLEND_STACK);
}
self.layers.push(blend);
} }
/// Pops the current layer. /// Pops the current layer.
pub fn pop_layer(&mut self) {} pub fn pop_layer(&mut self) {
if let Some(layer) = self.layers.pop() {
self.end_clip(Some(layer));
}
}
/// Fills a shape using the specified style and brush. /// Fills a shape using the specified style and brush.
pub fn fill<'s, E>( pub fn fill<'s, E>(
@ -79,8 +94,17 @@ impl<'a> Builder<'a> {
E::IntoIter: Clone, E::IntoIter: Clone,
E::Item: Borrow<Element>, E::Item: Borrow<Element>,
{ {
self.linewidth(-1.0);
let elements = elements.into_iter(); let elements = elements.into_iter();
self.encode_path(elements, true); self.encode_path(elements, true);
if let Some(brush_transform) = brush_transform {
self.transform(brush_transform);
self.swap_last_tags();
self.encode_brush(brush);
self.transform(brush_transform.inverse());
} else {
self.encode_brush(brush);
}
} }
/// Strokes a shape using the specified style and brush. /// Strokes a shape using the specified style and brush.
@ -96,8 +120,17 @@ impl<'a> Builder<'a> {
E::IntoIter: Clone, E::IntoIter: Clone,
E::Item: Borrow<Element>, E::Item: Borrow<Element>,
{ {
self.linewidth(style.width);
let elements = elements.into_iter(); let elements = elements.into_iter();
self.encode_path(elements, false); self.encode_path(elements, false);
if let Some(brush_transform) = brush_transform {
self.transform(brush_transform);
self.swap_last_tags();
self.encode_brush(brush);
self.transform(brush_transform.inverse());
} else {
self.encode_brush(brush);
}
} }
/// Appends a fragment to the scene. /// Appends a fragment to the scene.
@ -140,8 +173,14 @@ impl<'a> Builder<'a> {
} }
/// Completes construction and finalizes the underlying scene. /// Completes construction and finalizes the underlying scene.
pub fn finish(self) {} pub fn finish(self) {
while let Some(layer) = self.layers.pop() {
self.end_clip(Some(layer));
}
}
}
impl<'a> Builder<'a> {
fn encode_path<E>(&mut self, elements: E, is_fill: bool) fn encode_path<E>(&mut self, elements: E, is_fill: bool)
where where
E: Iterator, E: Iterator,
@ -183,6 +222,24 @@ impl<'a> Builder<'a> {
self.scene.n_pathseg += n_pathseg; self.scene.n_pathseg += n_pathseg;
} }
fn transform(&mut self, transform: Affine) {
self.scene.tag_stream.push(0x20);
self.scene.transform_stream.push(transform);
}
// 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) {
self.scene.tag_stream.push(0x40);
self.scene.linewidth_stream.push(linewidth);
}
fn encode_brush(&mut self, brush: &Brush) { fn encode_brush(&mut self, brush: &Brush) {
match brush { match brush {
Brush::Solid(color) => { Brush::Solid(color) => {
@ -237,6 +294,28 @@ impl<'a> Builder<'a> {
} }
} }
} }
/// Start a clip.
fn begin_clip(&mut self, blend: Option<Blend>) {
self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP);
let element = Clip {
blend: blend.unwrap_or(Blend::default()).pack(),
};
self.scene.drawdata_stream.extend(bytemuck::bytes_of(&element));
self.scene.n_clip += 1;
}
fn end_clip(&mut self, blend: Option<Blend>) {
self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP);
let element = Clip {
blend: blend.unwrap_or(Blend::default()).pack(),
};
self.scene.drawdata_stream.extend(bytemuck::bytes_of(&element));
// 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;
}
} }
enum ResourceData<'a> { enum ResourceData<'a> {
@ -244,6 +323,17 @@ enum ResourceData<'a> {
Scene(&'a mut ResourceContext), Scene(&'a mut ResourceContext),
} }
impl ResourceData<'_> {
fn clear(&mut self) {
match self {
Self::Fragment(res) {
res.patches.clear();
res.stops.clear();
}
}
}
}
// Tags for draw objects. See shader/drawtag.h for the authoritative source. // Tags for draw objects. See shader/drawtag.h for the authoritative source.
const DRAWTAG_FILLCOLOR: u32 = 0x44; const DRAWTAG_FILLCOLOR: u32 = 0x44;
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114; const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;