Merge branch 'main' into precompute
# Conflicts:
# sparse_strips/vello_bench/src/cpu_fine.rs
diff --git a/sparse_strips/vello_api/src/paint.rs b/sparse_strips/vello_api/src/paint.rs
index 13fb370..595d8f8 100644
--- a/sparse_strips/vello_api/src/paint.rs
+++ b/sparse_strips/vello_api/src/paint.rs
@@ -36,10 +36,10 @@
/// 2) Indexed paints, which can represent any arbitrary, more complex paint that is
/// determined by the frontend. The intended way of using this is to store a vector
/// of paints and store its index inside `IndexedPaint`.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq)]
pub enum Paint {
/// A premultiplied RGBA8 color.
- Solid(PremulRgba8),
+ Solid(PremulColor),
/// A paint that needs to be resolved via an index.
Indexed(IndexedPaint),
}
@@ -50,7 +50,7 @@
// Since we only do that conversion once per path it might not be critical, but should
// still be measured. This also applies to all other usages of `to_rgba8` in the current
// code.
- Self::Solid(value.premultiply().to_rgba8())
+ Self::Solid(PremulColor::new(value))
}
}
@@ -127,6 +127,40 @@
pub transform: Affine,
}
+/// A premultiplied color.
+#[derive(Debug, Clone, PartialEq, Copy)]
+pub struct PremulColor {
+ premul_u8: PremulRgba8,
+ premul_f32: peniko::color::PremulColor<Srgb>,
+}
+
+impl PremulColor {
+ /// Create a new premultiplied color.
+ pub fn new(color: AlphaColor<Srgb>) -> Self {
+ let premul = color.premultiply();
+
+ Self {
+ premul_u8: premul.to_rgba8(),
+ premul_f32: premul,
+ }
+ }
+
+ /// Return the color as a premultiplied RGBA8 color.
+ pub fn as_premul_rgba8(&self) -> PremulRgba8 {
+ self.premul_u8
+ }
+
+ /// Return the color as a premultiplied RGBAF32 color.
+ pub fn as_premul_f32(&self) -> peniko::color::PremulColor<Srgb> {
+ self.premul_f32
+ }
+
+ /// Return whether the color is opaque (i.e. has no transparency).
+ pub fn is_opaque(&self) -> bool {
+ self.premul_f32.components[3] == 1.0
+ }
+}
+
/// A kind of paint that can be used for filling and stroking shapes.
#[derive(Debug, Clone)]
pub enum PaintType {
diff --git a/sparse_strips/vello_bench/src/cpu_fine.rs b/sparse_strips/vello_bench/src/cpu_fine.rs
index 62e6bb3..013a420 100644
--- a/sparse_strips/vello_bench/src/cpu_fine.rs
+++ b/sparse_strips/vello_bench/src/cpu_fine.rs
@@ -9,9 +9,9 @@
use vello_common::coarse::WideTile;
use vello_common::color::DynamicColor;
use vello_common::color::palette::css::{BLUE, GREEN, RED, ROYAL_BLUE, YELLOW};
-use vello_common::encode::EncodeExt;
-use vello_common::kurbo::Point;
-use vello_common::paint::{Gradient, Paint};
+use vello_common::encode::{EncodeExt, EncodedPaint};
+use vello_common::kurbo::{Affine, Point};
+use vello_common::paint::{Gradient, Paint, PremulColor};
use vello_common::peniko;
use vello_common::peniko::{ColorStop, ColorStops, GradientKind};
use vello_common::tile::Tile;
@@ -21,12 +21,15 @@
let mut g = c.benchmark_group("fine/fill");
macro_rules! fill_single {
- ($name:ident, $paint:expr, $paints:expr) => {
+ ($name:ident, $paint:expr, $paints:expr, $width:expr) => {
g.bench_function(stringify!($name), |b| {
let mut fine = Fine::new(WideTile::WIDTH, Tile::HEIGHT);
+ let paint = $paint;
+ let paints: &[EncodedPaint] = $paints;
+
b.iter(|| {
- fine.fill(0, WideTile::WIDTH as usize, $paint, $paints);
+ fine.fill(0, $width, paint, paints);
std::hint::black_box(&fine);
})
@@ -36,13 +39,21 @@
fill_single!(
solid_opaque,
- &Paint::Solid(ROYAL_BLUE.premultiply().to_rgba8()),
- &[]
+ &Paint::Solid(PremulColor::new(ROYAL_BLUE)),
+ &[],
+ WideTile::WIDTH as usize
);
fill_single!(
- sold_transparent,
- &Paint::Solid(ROYAL_BLUE.with_alpha(0.2).premultiply().to_rgba8()),
- &[]
+ solid_opaque_short,
+ &Paint::Solid(PremulColor::new(ROYAL_BLUE)),
+ &[],
+ 16
+ );
+ fill_single!(
+ solid_transparent,
+ &Paint::Solid(PremulColor::new(ROYAL_BLUE.with_alpha(0.2))),
+ &[],
+ WideTile::WIDTH as usize
);
macro_rules! fill_single_linear {
@@ -60,7 +71,7 @@
let paint = grad.encode_into(&mut paints);
- fill_single!($name, &paint, &paints);
+ fill_single!($name, &paint, &paints, WideTile::WIDTH as usize);
};
}
@@ -101,7 +112,7 @@
let paint = grad.encode_into(&mut paints);
- fill_single!($name, &paint, &paints);
+ fill_single!($name, &paint, &paints, WideTile::WIDTH as usize);
};
}
@@ -146,7 +157,7 @@
let paint = grad.encode_into(&mut paints);
- fill_single!($name, &paint, &paints);
+ fill_single!($name, &paint, &paints, WideTile::WIDTH as usize);
};
}
@@ -183,12 +194,15 @@
}
macro_rules! strip_single {
- ($name:ident, $paint:expr, $paints:expr) => {
+ ($name:ident, $paint:expr, $paints:expr, $width:expr) => {
g.bench_function(stringify!($name), |b| {
let mut fine = Fine::new(WideTile::WIDTH, Tile::HEIGHT);
+ let paint = $paint;
+ let paints: &[EncodedPaint] = $paints;
+
b.iter(|| {
- fine.strip(0, WideTile::WIDTH as usize, &alphas, $paint, $paints);
+ fine.strip(0, $width, &alphas, paint, paints);
std::hint::black_box(&fine);
})
@@ -198,8 +212,16 @@
strip_single!(
basic,
- &Paint::Solid(ROYAL_BLUE.premultiply().to_rgba8()),
- &[]
+ &Paint::Solid(PremulColor::new(ROYAL_BLUE)),
+ &[],
+ WideTile::WIDTH as usize
+ );
+
+ strip_single!(
+ basic_short,
+ &Paint::Solid(PremulColor::new(ROYAL_BLUE)),
+ &[],
+ 8
);
// There is not really a need to measure performance of complex paint types
diff --git a/sparse_strips/vello_common/src/coarse.rs b/sparse_strips/vello_common/src/coarse.rs
index 3245aff..53fc368 100644
--- a/sparse_strips/vello_common/src/coarse.rs
+++ b/sparse_strips/vello_common/src/coarse.rs
@@ -3,14 +3,11 @@
//! Generating and processing wide tiles.
-use crate::{
- color::{AlphaColor, Srgb},
- strip::Strip,
- tile::Tile,
-};
+use crate::color::palette::css::TRANSPARENT;
+use crate::{strip::Strip, tile::Tile};
use alloc::vec;
use alloc::{boxed::Box, vec::Vec};
-use vello_api::color::PremulRgba8;
+use vello_api::paint::PremulColor;
use vello_api::{paint::Paint, peniko::Fill};
/// A container for wide tiles.
@@ -130,7 +127,7 @@
/// Reset all tiles in the container.
pub fn reset(&mut self) {
for tile in &mut self.tiles {
- tile.bg = AlphaColor::<Srgb>::TRANSPARENT.premultiply().to_rgba8();
+ tile.bg = PremulColor::new(TRANSPARENT);
tile.cmds.clear();
}
}
@@ -616,7 +613,7 @@
/// The y coordinate of the wide tile.
pub y: u16,
/// The background of the tile.
- pub bg: PremulRgba8,
+ pub bg: PremulColor,
/// The draw commands of the tile.
pub cmds: Vec<Cmd>,
@@ -635,7 +632,7 @@
Self {
x,
y,
- bg: AlphaColor::<Srgb>::TRANSPARENT.premultiply().to_rgba8(),
+ bg: PremulColor::new(TRANSPARENT),
cmds: vec![],
n_zero_clip: 0,
@@ -656,7 +653,8 @@
//
// However, the extra cost of tracking such optimizations may outweigh the
// benefit, especially in hybrid mode with GPU painting.
- let can_override = x == 0 && width == Self::WIDTH && s.a == 255 && self.n_clip == 0;
+ let can_override =
+ x == 0 && width == Self::WIDTH && s.is_opaque() && self.n_clip == 0;
can_override.then_some(*s)
} else {
// TODO: Implement for indexed paints.
diff --git a/sparse_strips/vello_cpu/src/fine/mod.rs b/sparse_strips/vello_cpu/src/fine/mod.rs
index e0717cb..7569f19 100644
--- a/sparse_strips/vello_cpu/src/fine/mod.rs
+++ b/sparse_strips/vello_cpu/src/fine/mod.rs
@@ -145,18 +145,18 @@
match fill {
Paint::Solid(color) => {
- let color = &color.to_u8_array();
+ let color = color.as_premul_rgba8().to_u8_array();
// If color is completely opaque we can just memcopy the colors.
if color[3] == 255 {
for t in blend_buf.chunks_exact_mut(COLOR_COMPONENTS) {
- t.copy_from_slice(color);
+ t.copy_from_slice(&color);
}
return;
}
- fill::src_over(blend_buf, iter::repeat(*color));
+ fill::src_over(blend_buf, iter::repeat(color));
}
Paint::Indexed(paint) => {
let encoded_paint = &encoded_paints[paint.index()];
@@ -223,7 +223,11 @@
match fill {
Paint::Solid(color) => {
- strip::src_over(blend_buf, iter::repeat(color.to_u8_array()), alphas);
+ strip::src_over(
+ blend_buf,
+ iter::repeat(color.as_premul_rgba8().to_u8_array()),
+ alphas,
+ );
}
Paint::Indexed(paint) => {
let encoded_paint = &paints[paint.index()];
diff --git a/sparse_strips/vello_cpu/src/render.rs b/sparse_strips/vello_cpu/src/render.rs
index 8a4b9a1..2dd7b00 100644
--- a/sparse_strips/vello_cpu/src/render.rs
+++ b/sparse_strips/vello_cpu/src/render.rs
@@ -198,7 +198,7 @@
let wtile = self.wide.get(x, y);
fine.set_coords(x, y);
- fine.clear(wtile.bg.to_u8_array());
+ fine.clear(wtile.bg.as_premul_rgba8().to_u8_array());
for cmd in &wtile.cmds {
fine.run_cmd(cmd, &self.alphas, &self.encoded_paints);
}
diff --git a/sparse_strips/vello_hybrid/src/scene.rs b/sparse_strips/vello_hybrid/src/scene.rs
index 952abf4..c7e382b 100644
--- a/sparse_strips/vello_hybrid/src/scene.rs
+++ b/sparse_strips/vello_hybrid/src/scene.rs
@@ -7,7 +7,6 @@
use alloc::vec;
use alloc::vec::Vec;
use vello_common::coarse::{Wide, WideTile};
-use vello_common::color::PremulRgba8;
use vello_common::flatten::Line;
use vello_common::glyph::{GlyphRenderer, GlyphRunBuilder, PreparedGlyph};
use vello_common::kurbo::{Affine, BezPath, Cap, Join, Rect, Shape, Stroke};
@@ -212,7 +211,7 @@
let wide_tile = &self.wide.tiles[wide_tile_idx];
let wide_tile_x = wide_tile_col * WideTile::WIDTH;
let wide_tile_y = wide_tile_row * Tile::HEIGHT;
- let bg = wide_tile.bg.to_u32();
+ let bg = wide_tile.bg.as_premul_rgba8().to_u32();
if bg != 0 {
strips.push(GpuStrip {
x: wide_tile_x,
@@ -226,8 +225,8 @@
for cmd in &wide_tile.cmds {
match cmd {
vello_common::coarse::Cmd::Fill(fill) => {
- let color: PremulRgba8 = match fill.paint {
- Paint::Solid(color) => color,
+ let rgba = match &fill.paint {
+ Paint::Solid(color) => color.as_premul_rgba8().to_u32(),
Paint::Indexed(_) => unimplemented!(),
};
strips.push(GpuStrip {
@@ -236,12 +235,12 @@
width: fill.width,
dense_width: 0,
col: 0,
- rgba: color.to_u32(),
+ rgba,
});
}
vello_common::coarse::Cmd::AlphaFill(cmd_strip) => {
- let color: PremulRgba8 = match cmd_strip.paint {
- Paint::Solid(color) => color,
+ let rgba = match &cmd_strip.paint {
+ Paint::Solid(color) => color.as_premul_rgba8().to_u32(),
Paint::Indexed(_) => unimplemented!(),
};
@@ -255,7 +254,7 @@
col: (cmd_strip.alpha_idx / usize::from(Tile::HEIGHT))
.try_into()
.expect(msg),
- rgba: color.to_u32(),
+ rgba,
});
}
_ => {