Address review feedback.

* remove area clamp in fine (now in #241)
* make DrawTag::info_size() const fn
* document DrawColor rgba field
* change Monoid type parameter to an associated type and describe the additional Default constraint
* consistent parens in PathTag::is_subpath_end
* add comments for 32-bit path segment types in PathTag

* also adds low level encoding functions for the three currently supported brushes
This commit is contained in:
Chad Brokaw 2023-01-08 17:10:05 -05:00
parent dbe7f27768
commit 520f6d0d41
5 changed files with 91 additions and 29 deletions

View file

@ -138,7 +138,7 @@ fn fill_path(tile: Tile, xy: vec2<f32>) -> array<f32, PIXELS_PER_THREAD> {
} }
// nonzero winding rule // nonzero winding rule
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { 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; return area;
} }

View file

@ -48,8 +48,8 @@ impl DrawTag {
} }
impl DrawTag { impl DrawTag {
/// Returns the size of the info buffer used by this tag. /// Returns the size of the info buffer (in u32s) used by this tag.
pub fn info_size(self) -> u32 { pub const fn info_size(self) -> u32 {
(self.0 >> 6) & 0xf (self.0 >> 6) & 0xf
} }
} }
@ -58,7 +58,8 @@ impl DrawTag {
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct DrawColor { pub struct DrawColor {
/// Packed RGBA color. /// Packed little endian RGBA premultiplied color with the alpha component
/// in the low byte.
pub rgba: u32, pub rgba: u32,
} }
@ -143,7 +144,9 @@ pub struct DrawMonoid {
pub info_offset: u32, pub info_offset: u32,
} }
impl Monoid<DrawTag> for DrawMonoid { impl Monoid for DrawMonoid {
type SourceValue = DrawTag;
fn new(tag: DrawTag) -> Self { fn new(tag: DrawTag) -> Self {
Self { Self {
path_ix: (tag != DrawTag::NOP) as u32, path_ix: (tag != DrawTag::NOP) as u32,

View file

@ -15,9 +15,11 @@
// Also licensed under MIT license, at your choice. // Also licensed under MIT license, at your choice.
use super::resource::Patch; 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. /// Encoded data streams for a scene.
#[derive(Default)] #[derive(Default)]
@ -148,44 +150,79 @@ impl Encoding {
/// Encodes a brush with an optional alpha modifier. /// Encodes a brush with an optional alpha modifier.
pub fn encode_brush<'b>(&mut self, brush: impl Into<BrushRef<'b>>, alpha: f32) { pub fn encode_brush<'b>(&mut self, brush: impl Into<BrushRef<'b>>, alpha: f32) {
use super::math::point_to_f32; use super::math::point_to_f32;
use super::{DrawColor, DrawLinearGradient, DrawRadialGradient};
match brush.into() { match brush.into() {
BrushRef::Solid(color) => { BrushRef::Solid(color) => {
self.draw_tags.push(DrawTag::COLOR);
let color = if alpha != 1.0 { let color = if alpha != 1.0 {
color_with_alpha(color, alpha) color_with_alpha(color, alpha)
} else { } else {
color color
}; };
self.draw_data self.encode_color(DrawColor::new(color));
.extend_from_slice(bytemuck::bytes_of(&DrawColor::new(color)));
} }
BrushRef::LinearGradient(gradient) => { BrushRef::LinearGradient(gradient) => {
self.add_ramp(&gradient.stops, alpha); self.encode_linear_gradient(
self.draw_tags.push(DrawTag::LINEAR_GRADIENT); DrawLinearGradient {
self.draw_data
.extend_from_slice(bytemuck::bytes_of(&DrawLinearGradient {
index: 0, index: 0,
p0: point_to_f32(gradient.start), p0: point_to_f32(gradient.start),
p1: point_to_f32(gradient.end), p1: point_to_f32(gradient.end),
})); },
gradient.stops.iter().copied(),
alpha,
gradient.extend,
);
} }
BrushRef::RadialGradient(gradient) => { BrushRef::RadialGradient(gradient) => {
self.add_ramp(&gradient.stops, alpha); self.encode_radial_gradient(
self.draw_tags.push(DrawTag::RADIAL_GRADIENT); DrawRadialGradient {
self.draw_data
.extend_from_slice(bytemuck::bytes_of(&DrawRadialGradient {
index: 0, index: 0,
p0: point_to_f32(gradient.start_center), p0: point_to_f32(gradient.start_center),
p1: point_to_f32(gradient.end_center), p1: point_to_f32(gradient.end_center),
r0: gradient.start_radius, r0: gradient.start_radius,
r1: gradient.end_radius, r1: gradient.end_radius,
})); },
gradient.stops.iter().copied(),
alpha,
gradient.extend,
);
} }
BrushRef::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"), 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<Item = ColorStop>,
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<Item = ColorStop>,
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. /// Encodes a begin clip command.
pub fn encode_begin_clip(&mut self, blend_mode: BlendMode, alpha: f32) { pub fn encode_begin_clip(&mut self, blend_mode: BlendMode, alpha: f32) {
use super::DrawBeginClip; use super::DrawBeginClip;
@ -211,16 +248,16 @@ impl Encoding {
self.path_tags.swap(len - 1, len - 2); 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<Item = ColorStop>, alpha: f32) {
let offset = self.draw_data.len(); let offset = self.draw_data.len();
let stops_start = self.color_stops.len(); let stops_start = self.color_stops.len();
if alpha != 1.0 { 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, offset: s.offset,
color: color_with_alpha(s.color, alpha), color: color_with_alpha(s.color, alpha),
})); }));
} else { } else {
self.color_stops.extend_from_slice(stops); self.color_stops.extend(color_stops);
} }
self.patches.push(Patch::Ramp { self.patches.push(Patch::Ramp {
offset, offset,

View file

@ -14,10 +14,15 @@
// //
// Also licensed under MIT license, at your choice. // Also licensed under MIT license, at your choice.
/// Interface for a monoid. /// Interface for a monoid. The default value must be the identity of
pub trait Monoid<T>: Default { /// the monoid.
pub trait Monoid: Default {
/// The source value for constructing the monoid.
type SourceValue;
/// Creates a monoid from a given value. /// 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. /// Combines two monoids. This operation must be associative.
fn combine(&self, other: &Self) -> Self; fn combine(&self, other: &Self) -> Self;
} }

View file

@ -30,6 +30,9 @@ pub struct PathSegment {
} }
/// Path segment type. /// 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)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Pod, Zeroable)]
#[repr(C)] #[repr(C)]
pub struct PathSegmentType(pub u8); pub struct PathSegmentType(pub u8);
@ -52,12 +55,18 @@ pub struct PathTag(pub u8);
impl PathTag { impl PathTag {
/// 32-bit floating point line segment. /// 32-bit floating point line segment.
///
/// This is equivalent to (PathSegmentType::LINE_TO | PathTag::F32_BIT).
pub const LINE_TO_F32: Self = Self(0x9); pub const LINE_TO_F32: Self = Self(0x9);
/// 32-bit floating point quadratic segment. /// 32-bit floating point quadratic segment.
///
/// This is equivalent to (PathSegmentType::QUAD_TO | PathTag::F32_BIT).
pub const QUAD_TO_F32: Self = Self(0xa); pub const QUAD_TO_F32: Self = Self(0xa);
/// 32-bit floating point cubic segment. /// 32-bit floating point cubic segment.
///
/// This is equivalent to (PathSegmentType::CUBIC_TO | PathTag::F32_BIT).
pub const CUBIC_TO_F32: Self = Self(0xb); pub const CUBIC_TO_F32: Self = Self(0xb);
/// 16-bit integral line segment. /// 16-bit integral line segment.
@ -78,8 +87,14 @@ impl PathTag {
/// Line width setting. /// Line width setting.
pub const LINEWIDTH: Self = Self(0x40); 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; const F32_BIT: u8 = 0x8;
/// Bit that marks a segment that is the end of a subpath.
const SUBPATH_END_BIT: u8 = 0x4; const SUBPATH_END_BIT: u8 = 0x4;
/// Mask for bottom 3 bits that contain the [PathSegmentType].
const SEGMENT_MASK: u8 = 0x3; const SEGMENT_MASK: u8 = 0x3;
/// Returns true if the tag is a segment. /// Returns true if the tag is a segment.
@ -94,7 +109,7 @@ impl PathTag {
/// Returns true if this segment ends a subpath. /// Returns true if this segment ends a subpath.
pub fn is_subpath_end(self) -> bool { 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. /// Sets the subpath end bit.
@ -124,7 +139,9 @@ pub struct PathMonoid {
pub path_ix: u32, pub path_ix: u32,
} }
impl Monoid<u32> for PathMonoid { impl Monoid for PathMonoid {
type SourceValue = u32;
/// Reduces a packed 32-bit word containing 4 tags. /// Reduces a packed 32-bit word containing 4 tags.
fn new(tag_word: u32) -> Self { fn new(tag_word: u32) -> Self {
let mut c = Self::default(); let mut c = Self::default();