Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sparse_strips/vello_bench/src/fine/fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub(crate) fn fill_single<S: Simd, N: FineKernel<S>>(
fine: &mut Fine<S, N>,
) {
b.iter(|| {
fine.fill(0, width, paint, blend_mode, encoded_paints, None);
fine.fill(0, width, paint, blend_mode, encoded_paints, None, None);

std::hint::black_box(&fine);
});
Expand Down
1 change: 1 addition & 0 deletions sparse_strips/vello_bench/src/fine/strip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ fn strip_single<S: Simd, N: FineKernel<S>>(
default_blend(),
encoded_paints,
Some(&alphas),
None,
);

std::hint::black_box(&fine);
Expand Down
34 changes: 27 additions & 7 deletions sparse_strips/vello_common/src/coarse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,13 @@ impl<const MODE: u8> Wide<MODE> {
/// - Generate alpha fill commands for the intersected wide tiles
/// 2. For active fill regions (determined by fill rule):
/// - Generate solid fill commands for the regions between strips
pub fn generate(&mut self, strip_buf: &[Strip], paint: Paint, thread_idx: u8) {
pub fn generate(
&mut self,
strip_buf: &[Strip],
paint: Paint,
thread_idx: u8,
mask: Option<Mask>,
) {
if strip_buf.is_empty() {
return;
}
Expand Down Expand Up @@ -307,6 +313,7 @@ impl<const MODE: u8> Wide<MODE> {
thread_idx,
paint: paint.clone(),
blend_mode: None,
mask: mask.clone(),
};
x += width;
col += u32::from(width);
Expand All @@ -332,8 +339,12 @@ impl<const MODE: u8> Wide<MODE> {
let x_wtile_rel = x % WideTile::WIDTH;
let width = x2.min((wtile_x + 1) * WideTile::WIDTH) - x;
x += width;
self.get_mut(wtile_x, strip_y)
.fill(x_wtile_rel, width, paint.clone());
self.get_mut(wtile_x, strip_y).fill(
x_wtile_rel,
width,
paint.clone(),
mask.clone(),
);
}
}
}
Expand Down Expand Up @@ -818,7 +829,7 @@ impl<const MODE: u8> WideTile<MODE> {
}
}

pub(crate) fn fill(&mut self, x: u16, width: u16, paint: Paint) {
pub(crate) fn fill(&mut self, x: u16, width: u16, paint: Paint, mask: Option<Mask>) {
if !self.is_zero_clip() {
match MODE {
MODE_CPU => {
Expand All @@ -836,6 +847,7 @@ impl<const MODE: u8> WideTile<MODE> {
let can_override = x == 0
&& width == WideTile::WIDTH
&& s.is_opaque()
&& mask.is_none()
&& self.n_clip == 0
&& self.n_bufs == 0;
can_override.then_some(*s)
Expand All @@ -853,6 +865,7 @@ impl<const MODE: u8> WideTile<MODE> {
width,
paint,
blend_mode: None,
mask,
}));
}
}
Expand All @@ -862,6 +875,7 @@ impl<const MODE: u8> WideTile<MODE> {
width,
paint,
blend_mode: None,
mask,
}));
}
_ => unreachable!(),
Expand Down Expand Up @@ -1004,6 +1018,8 @@ pub struct CmdFill {
pub paint: Paint,
/// The blend mode to apply before drawing the contents.
pub blend_mode: Option<BlendMode>,
/// A mask to apply to the command.
pub mask: Option<Mask>,
}

/// Fill a consecutive region of a wide tile with an alpha mask.
Expand All @@ -1023,6 +1039,8 @@ pub struct CmdAlphaFill {
pub paint: Paint,
/// A blend mode to apply before drawing the contents.
pub blend_mode: Option<BlendMode>,
/// A mask to apply to the command.
pub mask: Option<Mask>,
}

/// Same as fill, but copies top of clip stack to next on stack.
Expand Down Expand Up @@ -1097,11 +1115,13 @@ mod tests {
0,
10,
Paint::Solid(PremulColor::from_alpha_color(TRANSPARENT)),
None,
);
wide.fill(
10,
10,
Paint::Solid(PremulColor::from_alpha_color(TRANSPARENT)),
None,
);
wide.pop_buf();

Expand All @@ -1117,8 +1137,8 @@ mod tests {

let mut wide = WideTile::<MODE_CPU>::new(0, 0);
wide.push_buf();
wide.fill(0, 10, paint.clone());
wide.fill(10, 10, paint.clone());
wide.fill(0, 10, paint.clone(), None);
wide.fill(10, 10, paint.clone(), None);
wide.blend(blend_mode);
wide.pop_buf();

Expand All @@ -1134,7 +1154,7 @@ mod tests {

let mut wide = WideTile::<MODE_CPU>::new(0, 0);
wide.push_buf();
wide.fill(0, 10, paint.clone());
wide.fill(0, 10, paint.clone(), None);
wide.blend(blend_mode);
wide.pop_buf();

Expand Down
90 changes: 62 additions & 28 deletions sparse_strips/vello_common/src/mask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@

use crate::pixmap::Pixmap;
use alloc::sync::Arc;
use alloc::vec::Vec;

/// A mask.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Mask {
data: Arc<[u8]>,
#[derive(Debug, PartialEq, Eq)]
struct MaskRepr {
data: Vec<u8>,
width: u16,
height: u16,
}

/// A mask.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Mask(Arc<MaskRepr>);

impl Mask {
/// Create a new alpha mask from the pixmap.
pub fn new_alpha(pixmap: &Pixmap) -> Self {
Expand All @@ -25,55 +29,85 @@ impl Mask {
Self::new_with(pixmap, false)
}

/// Create a new mask from the given alpha data.
///
/// The `data` vector must be of length `width * height` exactly.
///
/// The pixels are in row-major order.
///
/// # Panics
///
/// Panics if the `data` vector is not of length `width * height`.
pub fn from_parts(data: Vec<u8>, width: u16, height: u16) -> Self {
assert_eq!(
data.len(),
usize::from(width) * usize::from(height),
"Expected `data` to have length of exactly `width * height`"
);

Self(Arc::new(MaskRepr {
data,
width,
height,
}))
}

fn new_with(pixmap: &Pixmap, alpha_mask: bool) -> Self {
let data = Arc::from_iter(pixmap.data().iter().map(|pixel| {
if alpha_mask {
pixel.a
} else {
let r = f32::from(pixel.r) / 255.;
let g = f32::from(pixel.g) / 255.;
let b = f32::from(pixel.b) / 255.;
let data = pixmap
.data()
.iter()
.map(|pixel| {
if alpha_mask {
pixel.a
} else {
let r = f32::from(pixel.r) / 255.;
let g = f32::from(pixel.g) / 255.;
let b = f32::from(pixel.b) / 255.;

// See CSS Masking Module Level 1 § 7.10.1
// <https://www.w3.org/TR/css-masking-1/#MaskValues>
// and Filter Effects Module Level 1 § 9.6
// <https://www.w3.org/TR/filter-effects-1/#elementdef-fecolormatrix>.
// Note r, g and b are premultiplied by alpha.
let luma = r * 0.2126 + g * 0.7152 + b * 0.0722;
#[expect(clippy::cast_possible_truncation, reason = "This cannot overflow")]
{
(luma * 255.0 + 0.5) as u8
// See CSS Masking Module Level 1 § 7.10.1
// <https://www.w3.org/TR/css-masking-1/#MaskValues>
// and Filter Effects Module Level 1 § 9.6
// <https://www.w3.org/TR/filter-effects-1/#elementdef-fecolormatrix>.
// Note r, g and b are premultiplied by alpha.
let luma = r * 0.2126 + g * 0.7152 + b * 0.0722;
#[expect(clippy::cast_possible_truncation, reason = "This cannot overflow")]
{
(luma * 255.0 + 0.5) as u8
}
}
}
}));
})
.collect::<Vec<u8>>();

Self {
Self(Arc::new(MaskRepr {
data,
width: pixmap.width(),
height: pixmap.height(),
}
}))
}

/// Return the width of the mask.
#[inline]
pub fn width(&self) -> u16 {
self.width
self.0.width
}

/// Return the height of the mask.
#[inline]
pub fn height(&self) -> u16 {
self.height
self.0.height
}

/// Sample the value at a specific location.
///
/// This function might panic or yield a wrong result if the location
/// is out-of-bounds.
#[inline(always)]
pub fn sample(&self, x: u16, y: u16) -> u8 {
debug_assert!(
x < self.width && y < self.height,
x < self.0.width && y < self.0.height,
"cannot sample mask outside of its range"
);

self.data[y as usize * self.width as usize + x as usize]
self.0.data[y as usize * self.0.width as usize + x as usize]
}
}
2 changes: 2 additions & 0 deletions sparse_strips/vello_cpu/src/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(crate) trait Dispatcher: Debug + Send + Sync {
transform: Affine,
paint: Paint,
aliasing_threshold: Option<u8>,
mask: Option<Mask>,
);
fn stroke_path(
&mut self,
Expand All @@ -34,6 +35,7 @@ pub(crate) trait Dispatcher: Debug + Send + Sync {
transform: Affine,
paint: Paint,
aliasing_threshold: Option<u8>,
mask: Option<Mask>,
);
fn push_layer(
&mut self,
Expand Down
14 changes: 13 additions & 1 deletion sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,17 +281,20 @@ impl MultiThreadedDispatcher {
strips: strip_range,
paint,
thread_id,
mask,
} => self.wide.generate(
&task.allocation_group.strips
[strip_range.start as usize..strip_range.end as usize],
paint.clone(),
thread_id,
mask,
),
CoarseTaskType::RenderWideCommand {
strips,
paint,
thread_id,
} => self.wide.generate(&strips, paint.clone(), thread_id),
mask,
} => self.wide.generate(&strips, paint.clone(), thread_id, mask),
CoarseTaskType::PushLayer {
thread_id,
clip_path,
Expand Down Expand Up @@ -387,6 +390,7 @@ impl Dispatcher for MultiThreadedDispatcher {
transform: Affine,
paint: Paint,
aliasing_threshold: Option<u8>,
mask: Option<Mask>,
) {
let start = self.allocation_group.path.len() as u32;
self.allocation_group.path.extend(path);
Expand All @@ -397,6 +401,7 @@ impl Dispatcher for MultiThreadedDispatcher {
paint,
fill_rule,
aliasing_threshold,
mask,
});
}

Expand All @@ -407,6 +412,7 @@ impl Dispatcher for MultiThreadedDispatcher {
transform: Affine,
paint: Paint,
aliasing_threshold: Option<u8>,
mask: Option<Mask>,
) {
let start = self.allocation_group.path.len() as u32;
self.allocation_group.path.extend(path);
Expand All @@ -417,6 +423,7 @@ impl Dispatcher for MultiThreadedDispatcher {
paint,
stroke: stroke.clone(),
aliasing_threshold,
mask,
});
}

Expand Down Expand Up @@ -696,6 +703,7 @@ pub(crate) enum RenderTaskType {
paint: Paint,
fill_rule: Fill,
aliasing_threshold: Option<u8>,
mask: Option<Mask>,
},
WideCommand {
strip_buf: Box<[Strip]>,
Expand All @@ -708,6 +716,7 @@ pub(crate) enum RenderTaskType {
paint: Paint,
stroke: Stroke,
aliasing_threshold: Option<u8>,
mask: Option<Mask>,
},
PushLayer {
clip_path: Option<(Range<u32>, Affine)>,
Expand All @@ -730,11 +739,13 @@ pub(crate) enum CoarseTaskType {
thread_id: u8,
strips: Range<u32>,
paint: Paint,
mask: Option<Mask>,
},
RenderWideCommand {
thread_id: u8,
strips: Box<[Strip]>,
paint: Paint,
mask: Option<Mask>,
},
PushLayer {
thread_id: u8,
Expand Down Expand Up @@ -811,6 +822,7 @@ mod tests {
Affine::IDENTITY,
Paint::Solid(PremulColor::from_alpha_color(BLUE)),
None,
None,
);
dispatcher.flush();
}
Expand Down
Loading