diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs index a40cc40..b71c293 100644 --- a/piet-scene/src/geometry.rs +++ b/piet-scene/src/geometry.rs @@ -115,6 +115,26 @@ impl Affine { 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 { diff --git a/piet-scene/src/resource/ramp_cache.rs b/piet-scene/src/resource/gradient.rs similarity index 100% rename from piet-scene/src/resource/ramp_cache.rs rename to piet-scene/src/resource/gradient.rs diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs index 36e6419..a811b99 100644 --- a/piet-scene/src/resource/mod.rs +++ b/piet-scene/src/resource/mod.rs @@ -1,12 +1,14 @@ -mod ramp_cache; +mod gradient; use crate::brush::{Brush, Stop}; -use ramp_cache::RampCache; +use gradient::RampCache; +use std::collections::HashMap; /// Context for caching resources across rendering operations. #[derive(Default)] pub struct ResourceContext { - ramp_cache: RampCache, + ramps: RampCache, + persistent_map: HashMap, } impl ResourceContext { @@ -15,14 +17,23 @@ impl ResourceContext { } 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 { - self.ramp_cache.add(stops) + self.ramps.add(stops) } pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush { + match brush { + Brush::Persistent(dup) => return *dup, + _ => {} + } PersistentBrush { kind: 0, id: 0 } } @@ -35,3 +46,8 @@ pub struct PersistentBrush { kind: u8, id: u64, } + +struct PersistentBrushData { + brush: Brush, + +} diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index 59e0a15..0f2c2a7 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -21,6 +21,8 @@ use crate::resource::ResourceContext; use bytemuck::{Pod, Zeroable}; use core::borrow::Borrow; +const MAX_BLEND_STACK: usize = 256; + /// Creates a new builder for constructing a scene. pub fn build_scene<'a>(scene: &'a mut Scene, resources: &'a mut ResourceContext) -> Builder<'a> { 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> { scene: &'a mut SceneData, resources: ResourceData<'a>, + layers: Vec, } impl<'a> Builder<'a> { /// Creates a new builder for constructing a scene. 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. @@ -60,12 +65,22 @@ impl<'a> Builder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.linewidth(-1.0); let elements = elements.into_iter(); 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. - 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. pub fn fill<'s, E>( @@ -79,8 +94,17 @@ impl<'a> Builder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.linewidth(-1.0); let elements = elements.into_iter(); 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. @@ -96,8 +120,17 @@ impl<'a> Builder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.linewidth(style.width); let elements = elements.into_iter(); 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. @@ -140,8 +173,14 @@ impl<'a> Builder<'a> { } /// 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(&mut self, elements: E, is_fill: bool) where E: Iterator, @@ -183,6 +222,24 @@ impl<'a> Builder<'a> { 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) { match brush { Brush::Solid(color) => { @@ -237,6 +294,28 @@ impl<'a> Builder<'a> { } } } + + /// Start a clip. + fn begin_clip(&mut self, blend: Option) { + 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) { + 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> { @@ -244,6 +323,17 @@ enum ResourceData<'a> { 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. const DRAWTAG_FILLCOLOR: u32 = 0x44; const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;