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
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;
}

View file

@ -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<DrawTag> for DrawMonoid {
impl Monoid for DrawMonoid {
type SourceValue = DrawTag;
fn new(tag: DrawTag) -> Self {
Self {
path_ix: (tag != DrawTag::NOP) as u32,

View file

@ -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<BrushRef<'b>>, 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<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.
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<Item = ColorStop>, 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,

View file

@ -14,10 +14,15 @@
//
// Also licensed under MIT license, at your choice.
/// Interface for a monoid.
pub trait Monoid<T>: 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;
}

View file

@ -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<u32> 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();