diff --git a/shader/fine.wgsl b/shader/fine.wgsl index edce31e..7b298ca 100644 --- a/shader/fine.wgsl +++ b/shader/fine.wgsl @@ -138,7 +138,7 @@ fn fill_path(tile: Tile, xy: vec2) -> array { } // nonzero winding rule for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { - area[i] = min(abs(area[i]), 1.0); + area[i] = abs(area[i]); } return area; } diff --git a/src/encoding/draw.rs b/src/encoding/draw.rs index acb9b1c..1ddaead 100644 --- a/src/encoding/draw.rs +++ b/src/encoding/draw.rs @@ -48,8 +48,8 @@ impl DrawTag { } impl DrawTag { - /// Returns the size of the info buffer used by this tag. - pub fn info_size(self) -> u32 { + /// Returns the size of the info buffer (in u32s) used by this tag. + pub const fn info_size(self) -> u32 { (self.0 >> 6) & 0xf } } @@ -58,7 +58,8 @@ impl DrawTag { #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] #[repr(C)] pub struct DrawColor { - /// Packed RGBA color. + /// Packed little endian RGBA premultiplied color with the alpha component + /// in the low byte. pub rgba: u32, } @@ -143,7 +144,9 @@ pub struct DrawMonoid { pub info_offset: u32, } -impl Monoid for DrawMonoid { +impl Monoid for DrawMonoid { + type SourceValue = DrawTag; + fn new(tag: DrawTag) -> Self { Self { path_ix: (tag != DrawTag::NOP) as u32, diff --git a/src/encoding/encoding.rs b/src/encoding/encoding.rs index 4ba6fdb..c94e043 100644 --- a/src/encoding/encoding.rs +++ b/src/encoding/encoding.rs @@ -15,9 +15,11 @@ // Also licensed under MIT license, at your choice. use super::resource::Patch; -use super::{DrawTag, PathEncoder, PathTag, Transform}; +use super::{ + DrawColor, DrawLinearGradient, DrawRadialGradient, DrawTag, PathEncoder, PathTag, Transform, +}; -use peniko::{kurbo::Shape, BlendMode, BrushRef, Color, ColorStop}; +use peniko::{kurbo::Shape, BlendMode, BrushRef, Color, ColorStop, Extend}; /// Encoded data streams for a scene. #[derive(Default)] @@ -148,44 +150,79 @@ impl Encoding { /// Encodes a brush with an optional alpha modifier. pub fn encode_brush<'b>(&mut self, brush: impl Into>, alpha: f32) { use super::math::point_to_f32; - use super::{DrawColor, DrawLinearGradient, DrawRadialGradient}; match brush.into() { BrushRef::Solid(color) => { - self.draw_tags.push(DrawTag::COLOR); let color = if alpha != 1.0 { color_with_alpha(color, alpha) } else { color }; - self.draw_data - .extend_from_slice(bytemuck::bytes_of(&DrawColor::new(color))); + self.encode_color(DrawColor::new(color)); } BrushRef::LinearGradient(gradient) => { - self.add_ramp(&gradient.stops, alpha); - self.draw_tags.push(DrawTag::LINEAR_GRADIENT); - self.draw_data - .extend_from_slice(bytemuck::bytes_of(&DrawLinearGradient { + self.encode_linear_gradient( + DrawLinearGradient { index: 0, p0: point_to_f32(gradient.start), p1: point_to_f32(gradient.end), - })); + }, + gradient.stops.iter().copied(), + alpha, + gradient.extend, + ); } BrushRef::RadialGradient(gradient) => { - self.add_ramp(&gradient.stops, alpha); - self.draw_tags.push(DrawTag::RADIAL_GRADIENT); - self.draw_data - .extend_from_slice(bytemuck::bytes_of(&DrawRadialGradient { + self.encode_radial_gradient( + DrawRadialGradient { index: 0, p0: point_to_f32(gradient.start_center), p1: point_to_f32(gradient.end_center), r0: gradient.start_radius, r1: gradient.end_radius, - })); + }, + gradient.stops.iter().copied(), + alpha, + gradient.extend, + ); } BrushRef::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"), } } + /// Encodes a solid color brush. + pub fn encode_color(&mut self, color: DrawColor) { + self.draw_tags.push(DrawTag::COLOR); + self.draw_data.extend_from_slice(bytemuck::bytes_of(&color)); + } + + /// Encodes a linear gradient brush. + pub fn encode_linear_gradient( + &mut self, + gradient: DrawLinearGradient, + color_stops: impl Iterator, + alpha: f32, + _extend: Extend, + ) { + self.add_ramp(color_stops, alpha); + self.draw_tags.push(DrawTag::LINEAR_GRADIENT); + self.draw_data + .extend_from_slice(bytemuck::bytes_of(&gradient)); + } + + /// Encodes a radial gradient brush. + pub fn encode_radial_gradient( + &mut self, + gradient: DrawRadialGradient, + color_stops: impl Iterator, + alpha: f32, + _extend: Extend, + ) { + self.add_ramp(color_stops, alpha); + self.draw_tags.push(DrawTag::RADIAL_GRADIENT); + self.draw_data + .extend_from_slice(bytemuck::bytes_of(&gradient)); + } + /// Encodes a begin clip command. pub fn encode_begin_clip(&mut self, blend_mode: BlendMode, alpha: f32) { use super::DrawBeginClip; @@ -211,16 +248,16 @@ impl Encoding { self.path_tags.swap(len - 1, len - 2); } - fn add_ramp(&mut self, stops: &[ColorStop], alpha: f32) { + fn add_ramp(&mut self, color_stops: impl Iterator, alpha: f32) { let offset = self.draw_data.len(); let stops_start = self.color_stops.len(); if alpha != 1.0 { - self.color_stops.extend(stops.iter().map(|s| ColorStop { + self.color_stops.extend(color_stops.map(|s| ColorStop { offset: s.offset, color: color_with_alpha(s.color, alpha), })); } else { - self.color_stops.extend_from_slice(stops); + self.color_stops.extend(color_stops); } self.patches.push(Patch::Ramp { offset, diff --git a/src/encoding/monoid.rs b/src/encoding/monoid.rs index ce3bd8a..37bca92 100644 --- a/src/encoding/monoid.rs +++ b/src/encoding/monoid.rs @@ -14,10 +14,15 @@ // // Also licensed under MIT license, at your choice. -/// Interface for a monoid. -pub trait Monoid: Default { +/// Interface for a monoid. The default value must be the identity of +/// the monoid. +pub trait Monoid: Default { + /// The source value for constructing the monoid. + type SourceValue; + /// Creates a monoid from a given value. - fn new(value: T) -> Self; + fn new(value: Self::SourceValue) -> Self; + /// Combines two monoids. This operation must be associative. fn combine(&self, other: &Self) -> Self; } diff --git a/src/encoding/path.rs b/src/encoding/path.rs index a0a01eb..7d91072 100644 --- a/src/encoding/path.rs +++ b/src/encoding/path.rs @@ -30,6 +30,9 @@ pub struct PathSegment { } /// Path segment type. +/// +/// The values of the segment types are equivalent to the number of associated +/// points for each segment in the path data stream. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Pod, Zeroable)] #[repr(C)] pub struct PathSegmentType(pub u8); @@ -52,12 +55,18 @@ pub struct PathTag(pub u8); impl PathTag { /// 32-bit floating point line segment. + /// + /// This is equivalent to (PathSegmentType::LINE_TO | PathTag::F32_BIT). pub const LINE_TO_F32: Self = Self(0x9); /// 32-bit floating point quadratic segment. + /// + /// This is equivalent to (PathSegmentType::QUAD_TO | PathTag::F32_BIT). pub const QUAD_TO_F32: Self = Self(0xa); /// 32-bit floating point cubic segment. + /// + /// This is equivalent to (PathSegmentType::CUBIC_TO | PathTag::F32_BIT). pub const CUBIC_TO_F32: Self = Self(0xb); /// 16-bit integral line segment. @@ -78,8 +87,14 @@ impl PathTag { /// Line width setting. pub const LINEWIDTH: Self = Self(0x40); + /// Bit for path segments that are represented as f32 values. If unset + /// they are represented as i16. const F32_BIT: u8 = 0x8; + + /// Bit that marks a segment that is the end of a subpath. const SUBPATH_END_BIT: u8 = 0x4; + + /// Mask for bottom 3 bits that contain the [PathSegmentType]. const SEGMENT_MASK: u8 = 0x3; /// Returns true if the tag is a segment. @@ -94,7 +109,7 @@ impl PathTag { /// Returns true if this segment ends a subpath. pub fn is_subpath_end(self) -> bool { - (self.0 & Self::SUBPATH_END_BIT) != 0 + self.0 & Self::SUBPATH_END_BIT != 0 } /// Sets the subpath end bit. @@ -124,7 +139,9 @@ pub struct PathMonoid { pub path_ix: u32, } -impl Monoid for PathMonoid { +impl Monoid for PathMonoid { + type SourceValue = u32; + /// Reduces a packed 32-bit word containing 4 tags. fn new(tag_word: u32) -> Self { let mut c = Self::default();