impl
diff --git a/sparse_strips/vello_bench/src/fine/strip.rs b/sparse_strips/vello_bench/src/fine/strip.rs index 56f728e..7740238 100644 --- a/sparse_strips/vello_bench/src/fine/strip.rs +++ b/sparse_strips/vello_bench/src/fine/strip.rs
@@ -16,14 +16,21 @@ use vello_dev_macros::vello_bench; pub fn strip(c: &mut Criterion) { - solid_single(c); - solid_short(c); - solid_medium(c); - solid_long(c); + solid_opaque_single(c); + solid_transparent_single(c); + + solid_opaque_short(c); + solid_transparent_short(c); + + solid_opaque_medium(c); + solid_transparent_medium(c); + + solid_opaque_long(c); + solid_transparent_long(c); } #[vello_bench] -pub fn solid_single<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { +pub fn solid_opaque_single<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE)); let width = Tile::WIDTH; @@ -31,7 +38,7 @@ } #[vello_bench] -pub fn solid_short<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { +pub fn solid_opaque_short<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE)); let width = 8; @@ -39,7 +46,7 @@ } #[vello_bench] -pub fn solid_medium<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { +pub fn solid_opaque_medium<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE)); let width = 16; @@ -47,13 +54,57 @@ } #[vello_bench] -pub fn solid_long<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { +pub fn solid_opaque_long<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) { let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE)); let width = 64; strip_single(&paint, &[], width, b, fine); } +#[vello_bench] +pub fn solid_transparent_single<S: Simd, N: FineKernel<S>>( + b: &mut Bencher<'_>, + fine: &mut Fine<S, N>, +) { + let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3))); + let width = Tile::WIDTH; + + strip_single(&paint, &[], width as usize, b, fine); +} + +#[vello_bench] +pub fn solid_transparent_short<S: Simd, N: FineKernel<S>>( + b: &mut Bencher<'_>, + fine: &mut Fine<S, N>, +) { + let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3))); + let width = 8; + + strip_single(&paint, &[], width, b, fine); +} + +#[vello_bench] +pub fn solid_transparent_medium<S: Simd, N: FineKernel<S>>( + b: &mut Bencher<'_>, + fine: &mut Fine<S, N>, +) { + let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3))); + let width = 16; + + strip_single(&paint, &[], width, b, fine); +} + +#[vello_bench] +pub fn solid_transparent_long<S: Simd, N: FineKernel<S>>( + b: &mut Bencher<'_>, + fine: &mut Fine<S, N>, +) { + let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3))); + let width = 64; + + strip_single(&paint, &[], width, b, fine); +} + fn strip_single<S: Simd, N: FineKernel<S>>( paint: &Paint, encoded_paints: &[EncodedPaint],
diff --git a/sparse_strips/vello_cpu/src/fine/highp/mod.rs b/sparse_strips/vello_cpu/src/fine/highp/mod.rs index 0948001..cca437a 100644 --- a/sparse_strips/vello_cpu/src/fine/highp/mod.rs +++ b/sparse_strips/vello_cpu/src/fine/highp/mod.rs
@@ -213,6 +213,21 @@ } } + #[inline(always)] + fn alpha_composite_opaque_solid( + simd: S, + dest: &mut [Self::Numeric], + src: [Self::Numeric; 4], + alphas: &[u8], + ) { + alpha_fill::alpha_composite_opaque_solid( + simd, + dest, + src, + bytemuck::cast_slice::<u8, [u8; 4]>(alphas).iter().copied(), + ); + } + /// Composites a source buffer onto a destination buffer using alpha blending. /// /// Dispatches to either the masked or unmasked implementation based on the @@ -446,6 +461,32 @@ ); } + /// Composites an opaque solid color with alpha masks. + #[inline(always)] + pub(super) fn alpha_composite_opaque_solid<S: Simd>( + s: S, + dest: &mut [f32], + src: [f32; 4], + alphas: impl Iterator<Item = [u8; 4]>, + ) { + s.vectorize( + #[inline(always)] + || { + let src_c = f32x16::block_splat(src.simd_into(s)); + let one = f32x16::splat(s, 1.0); + + for (next_dest, next_mask) in dest.chunks_exact_mut(16).zip(alphas) { + let bg_c = f32x16::from_slice(s, next_dest); + let mask_a = extract_masks(s, &next_mask); + let inv_mask_a = one - mask_a; + + let res = bg_c.mul_add(inv_mask_a, src_c * mask_a); + res.store_slice(next_dest); + } + }, + ); + } + /// Composites a buffer of colors with per-pixel alpha masks. /// /// Each pixel's source alpha is modulated by its corresponding mask value.
diff --git a/sparse_strips/vello_cpu/src/fine/lowp/mod.rs b/sparse_strips/vello_cpu/src/fine/lowp/mod.rs index c59d681..ca5655c 100644 --- a/sparse_strips/vello_cpu/src/fine/lowp/mod.rs +++ b/sparse_strips/vello_cpu/src/fine/lowp/mod.rs
@@ -249,6 +249,21 @@ } } + #[inline(always)] + fn alpha_composite_opaque_solid( + simd: S, + dest: &mut [Self::Numeric], + src: [Self::Numeric; 4], + alphas: &[u8], + ) { + alpha_fill::alpha_composite_opaque_solid( + simd, + dest, + src, + cast_slice::<u8, [u8; 8]>(alphas).iter().copied(), + ); + } + /// Composites a source buffer onto a destination buffer using alpha blending. /// /// Dispatches to either the masked or unmasked implementation based on the @@ -509,6 +524,35 @@ ); } + /// Composites an opaque solid color with alpha masks. + #[inline(always)] + pub(super) fn alpha_composite_opaque_solid<S: Simd>( + s: S, + dest: &mut [u8], + src: [u8; 4], + alphas: impl Iterator<Item = [u8; 8]>, + ) { + s.vectorize( + #[inline(always)] + || { + let src_c = u32x8::splat(s, u32::from_ne_bytes(src)).to_bytes(); + let one = u8x32::splat(s, 255); + + for (next_bg, next_mask) in dest.chunks_exact_mut(32).zip(alphas) { + let bg_v = u8x32::from_slice(s, next_bg); + let mask_v = extract_masks(s, &next_mask); + let inv_mask = one - mask_v; + + let p1 = s.widen_u8x32(bg_v) * s.widen_u8x32(inv_mask); + let p2 = s.widen_u8x32(src_c) * s.widen_u8x32(mask_v); + let res = s.narrow_u16x32((p1 + p2).div_255()); + + res.store_slice(next_bg); + } + }, + ); + } + /// Composites a buffer of colors with per-pixel alpha masks. /// /// Each pixel's source alpha is modulated by its corresponding mask value.
diff --git a/sparse_strips/vello_cpu/src/fine/mod.rs b/sparse_strips/vello_cpu/src/fine/mod.rs index c16c828..3ff979e 100644 --- a/sparse_strips/vello_cpu/src/fine/mod.rs +++ b/sparse_strips/vello_cpu/src/fine/mod.rs
@@ -418,6 +418,17 @@ alphas: Option<&[u8]>, ); + /// Perform alpha compositing with an opaque solid color and per-pixel alpha values. + /// + /// Blends an opaque RGBA color over the destination while modulating the source + /// contribution by the provided per-pixel alpha values. + fn alpha_composite_opaque_solid( + simd: S, + target: &mut [Self::Numeric], + src: [Self::Numeric; 4], + alphas: &[u8], + ); + /// Perform alpha compositing with a source buffer over the destination buffer. /// /// Blends the source buffer contents over the destination using standard alpha compositing. @@ -687,14 +698,14 @@ Paint::Solid(color) => { let color = T::extract_color(*color); - // If color is completely opaque, we can just directly override - // the blend buffer. - if color[3] == T::Numeric::ONE - && default_blend - && alphas.is_none() - && mask.is_none() - { - T::copy_solid(self.simd, blend_buf, color); + // If color is completely opaque and we have the default blend + // mode, we can use the solid-color fast path. + if color[3] == T::Numeric::ONE && default_blend && mask.is_none() { + if let Some(alphas) = alphas { + T::alpha_composite_opaque_solid(self.simd, blend_buf, color, alphas); + } else { + T::copy_solid(self.simd, blend_buf, color); + } return; }