Pass in `transform` for paint types separately By passing the paint transform in separately, the transform can be moved out of the paint types. This allows using existing ecosystem types directly (like `peniko::Gradient`).
diff --git a/sparse_strips/vello_api/src/paint.rs b/sparse_strips/vello_api/src/paint.rs index 8ce558b..0185e95 100644 --- a/sparse_strips/vello_api/src/paint.rs +++ b/sparse_strips/vello_api/src/paint.rs
@@ -3,11 +3,12 @@ //! Types for paints. -use crate::kurbo::{Affine, Point}; use crate::pixmap::Pixmap; use alloc::sync::Arc; -use peniko::color::{AlphaColor, ColorSpaceTag, HueDirection, PremulRgba8, Srgb}; -use peniko::{ColorStops, GradientKind, ImageQuality}; +use peniko::{ + Gradient, ImageQuality, + color::{AlphaColor, PremulRgba8, Srgb}, +}; /// A paint that needs to be resolved via its index. // In the future, we might add additional flags, that's why we have @@ -54,64 +55,6 @@ } } -// TODO: Replace this with the peniko type, once it supports transforms. -/// A gradient. -#[derive(Debug, Clone)] -pub struct Gradient { - /// The underlying kind of gradient. - pub kind: GradientKind, - /// The stops that makes up the gradient. - /// - /// Note that the first stop must have an offset of 0.0 and the last stop - /// must have an offset of 1.0. In addition to that, the stops must be sorted - /// with offsets in ascending order. - pub stops: ColorStops, - /// A transformation to apply to the gradient. - pub transform: Affine, - /// The extend of the gradient. - pub extend: peniko::Extend, - /// The color space to be used for interpolation. - /// - /// The colors in the color stops will be converted to this color space. - /// - /// This defaults to [sRGB](ColorSpaceTag::Srgb). - pub interpolation_cs: ColorSpaceTag, - /// When interpolating within a cylindrical color space, the direction for the hue. - /// - /// This is interpreted as described in [CSS Color Module Level 4 § 12.4]. - /// - /// [CSS Color Module Level 4 § 12.4]: https://drafts.csswg.org/css-color/#hue-interpolation - pub hue_direction: HueDirection, -} - -impl Default for Gradient { - fn default() -> Self { - Self { - kind: GradientKind::Linear { - start: Point::default(), - end: Point::default(), - }, - transform: Affine::IDENTITY, - interpolation_cs: ColorSpaceTag::Srgb, - extend: Default::default(), - hue_direction: Default::default(), - stops: Default::default(), - } - } -} - -impl Gradient { - /// Returns the gradient with the alpha component for all color stops - /// multiplied by `alpha`. - #[must_use] - pub fn multiply_alpha(mut self, alpha: f32) -> Self { - self.stops - .iter_mut() - .for_each(|stop| *stop = stop.multiply_alpha(alpha)); - self - } -} - /// An image. #[derive(Debug, Clone)] pub struct Image { @@ -123,8 +66,6 @@ pub y_extend: peniko::Extend, /// Hint for desired rendering quality. pub quality: ImageQuality, - /// A transform to apply to the image. - pub transform: Affine, } /// A premultiplied color.
diff --git a/sparse_strips/vello_bench/src/cpu_fine.rs b/sparse_strips/vello_bench/src/cpu_fine.rs index 25caa3f..d3a125d 100644 --- a/sparse_strips/vello_bench/src/cpu_fine.rs +++ b/sparse_strips/vello_bench/src/cpu_fine.rs
@@ -10,10 +10,12 @@ use vello_common::color::DynamicColor; use vello_common::color::palette::css::{BLUE, GREEN, RED, ROYAL_BLUE, YELLOW}; use vello_common::encode::{EncodeExt, EncodedPaint}; -use vello_common::kurbo::Point; -use vello_common::paint::{Gradient, Paint, PremulColor}; +use vello_common::kurbo::{Affine, Point}; +use vello_common::paint::{Paint, PremulColor}; use vello_common::peniko; -use vello_common::peniko::{BlendMode, ColorStop, ColorStops, Compose, GradientKind, Mix}; +use vello_common::peniko::{ + BlendMode, ColorStop, ColorStops, Compose, Gradient, GradientKind, Mix, +}; use vello_common::tile::Tile; use vello_cpu::fine::{Fine, SCRATCH_BUF_SIZE}; @@ -75,7 +77,7 @@ ..Default::default() }; - let paint = grad.encode_into(&mut paints); + let paint = grad.encode_into(&mut paints, Affine::IDENTITY); fill_single!($name, &paint, &paints, WideTile::WIDTH as usize); }; @@ -116,7 +118,7 @@ ..Default::default() }; - let paint = grad.encode_into(&mut paints); + let paint = grad.encode_into(&mut paints, Affine::IDENTITY); fill_single!($name, &paint, &paints, WideTile::WIDTH as usize); }; @@ -161,7 +163,7 @@ ..Default::default() }; - let paint = grad.encode_into(&mut paints); + let paint = grad.encode_into(&mut paints, Affine::IDENTITY); fill_single!($name, &paint, &paints, WideTile::WIDTH as usize); };
diff --git a/sparse_strips/vello_common/src/encode.rs b/sparse_strips/vello_common/src/encode.rs index 9501ba7..326ede1 100644 --- a/sparse_strips/vello_common/src/encode.rs +++ b/sparse_strips/vello_common/src/encode.rs
@@ -7,7 +7,7 @@ use crate::color::{ColorSpaceTag, HueDirection, PremulColor, Srgb, gradient}; use crate::encode::private::Sealed; use crate::kurbo::{Affine, Point, Vec2}; -use crate::peniko::{ColorStop, Extend, GradientKind, ImageQuality}; +use crate::peniko::{ColorStop, Extend, Gradient, GradientKind, ImageQuality}; use crate::pixmap::Pixmap; use alloc::borrow::Cow; use alloc::sync::Arc; @@ -15,7 +15,7 @@ use core::f32::consts::PI; use core::iter; use smallvec::SmallVec; -use vello_api::paint::{Gradient, Image, IndexedPaint, Paint}; +use vello_api::paint::{Image, IndexedPaint, Paint}; const DEGENERATE_THRESHOLD: f32 = 1.0e-6; const NUDGE_VAL: f32 = 1.0e-7; @@ -24,12 +24,12 @@ pub trait EncodeExt: private::Sealed { /// Encode the gradient and push it into a vector of encoded paints, returning /// the corresponding paint in the process. This will also validate the gradient. - fn encode_into(&self, paints: &mut Vec<EncodedPaint>) -> Paint; + fn encode_into(&self, paints: &mut Vec<EncodedPaint>, transform: Affine) -> Paint; } impl EncodeExt for Gradient { /// Encode the gradient into a paint. - fn encode_into(&self, paints: &mut Vec<EncodedPaint>) -> Paint { + fn encode_into(&self, paints: &mut Vec<EncodedPaint>, transform: Affine) -> Paint { // First make sure that the gradient is valid and not degenerate. if let Err(paint) = validate(self) { return paint; @@ -205,8 +205,8 @@ // adding 0.5. // Finally, we need to apply the _inverse_ transform to the point so that we can account // for the transform on the gradient. - let transform = Affine::translate((x_offset as f64 + 0.5, y_offset as f64 + 0.5)) - * self.transform.inverse(); + let transform = + Affine::translate((x_offset as f64 + 0.5, y_offset as f64 + 0.5)) * transform.inverse(); // One possible approach of calculating the positions would be to apply the above // transform to _each_ pixel that we render in the wide tile. However, a much better @@ -460,10 +460,10 @@ impl Sealed for Image {} impl EncodeExt for Image { - fn encode_into(&self, paints: &mut Vec<EncodedPaint>) -> Paint { + fn encode_into(&self, paints: &mut Vec<EncodedPaint>, transform: Affine) -> Paint { let idx = paints.len(); - let transform = self.transform.inverse(); + let transform = transform.inverse(); // TODO: This is somewhat expensive for large images, maybe it's not worth optimizing // non-opaque images in the first place.. let has_opacities = self.pixmap.data().chunks(4).any(|c| c[3] != 255); @@ -702,12 +702,10 @@ } mod private { - use vello_api::paint::Gradient; - #[allow(unnameable_types, reason = "We make it unnameable on purpose")] pub trait Sealed {} - impl Sealed for Gradient {} + impl Sealed for super::Gradient {} } #[cfg(test)] @@ -719,6 +717,7 @@ use crate::peniko::{ColorStop, ColorStops, GradientKind}; use alloc::vec; use smallvec::smallvec; + use vello_api::kurbo::Affine; #[test] fn gradient_missing_stops() { @@ -732,7 +731,10 @@ ..Default::default() }; - assert_eq!(gradient.encode_into(&mut buf), BLACK.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + BLACK.into() + ); } #[test] @@ -752,7 +754,10 @@ }; // Should return the color of the first stop. - assert_eq!(gradient.encode_into(&mut buf), GREEN.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + GREEN.into() + ); } #[test] @@ -777,7 +782,10 @@ ..Default::default() }; - assert_eq!(gradient.encode_into(&mut buf), GREEN.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + GREEN.into() + ); } #[test] @@ -802,7 +810,10 @@ ..Default::default() }; - assert_eq!(gradient.encode_into(&mut buf), GREEN.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + GREEN.into() + ); } #[test] @@ -827,7 +838,10 @@ ..Default::default() }; - assert_eq!(gradient.encode_into(&mut buf), GREEN.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + GREEN.into() + ); } #[test] @@ -853,7 +867,10 @@ ..Default::default() }; - assert_eq!(gradient.encode_into(&mut buf), GREEN.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + GREEN.into() + ); } #[test] @@ -880,6 +897,9 @@ ..Default::default() }; - assert_eq!(gradient.encode_into(&mut buf), GREEN.into()); + assert_eq!( + gradient.encode_into(&mut buf, Affine::IDENTITY), + GREEN.into() + ); } }
diff --git a/sparse_strips/vello_cpu/src/render.rs b/sparse_strips/vello_cpu/src/render.rs index 26c70d7..3bab858 100644 --- a/sparse_strips/vello_cpu/src/render.rs +++ b/sparse_strips/vello_cpu/src/render.rs
@@ -33,6 +33,7 @@ pub(crate) tiles: Tiles, pub(crate) strip_buf: Vec<Strip>, pub(crate) paint: PaintType, + pub(crate) paint_transform: Affine, pub(crate) stroke: Stroke, pub(crate) transform: Affine, pub(crate) fill_rule: Fill, @@ -52,6 +53,7 @@ let transform = Affine::IDENTITY; let fill_rule = Fill::NonZero; let paint = BLACK.into(); + let paint_transform = Affine::IDENTITY; let stroke = Stroke { width: 1.0, join: Join::Bevel, @@ -71,6 +73,7 @@ strip_buf, transform, paint, + paint_transform, fill_rule, stroke, encoded_paints, @@ -80,15 +83,17 @@ fn encode_current_paint(&mut self) -> Paint { match self.paint.clone() { PaintType::Solid(s) => s.into(), - PaintType::Gradient(mut g) => { + PaintType::Gradient(g) => { // TODO: Add caching? - g.transform = self.transform * g.transform; - g.encode_into(&mut self.encoded_paints) + g.encode_into( + &mut self.encoded_paints, + self.transform * self.paint_transform, + ) } - PaintType::Image(mut i) => { - i.transform = self.transform * i.transform; - i.encode_into(&mut self.encoded_paints) - } + PaintType::Image(i) => i.encode_into( + &mut self.encoded_paints, + self.transform * self.paint_transform, + ), } } @@ -196,6 +201,16 @@ self.paint = paint.into(); } + /// Set the current paint transform. + pub fn set_paint_transform(&mut self, paint_transform: Affine) { + self.paint_transform = paint_transform; + } + + /// Reset the current paint transform. + pub fn reset_paint_transform(&mut self) { + self.paint_transform = Affine::IDENTITY; + } + /// Set the current fill rule. pub fn set_fill_rule(&mut self, fill_rule: Fill) { self.fill_rule = fill_rule;
diff --git a/sparse_strips/vello_cpu/tests/gradient.rs b/sparse_strips/vello_cpu/tests/gradient.rs index da6e97e..38595ed 100644 --- a/sparse_strips/vello_cpu/tests/gradient.rs +++ b/sparse_strips/vello_cpu/tests/gradient.rs
@@ -6,8 +6,7 @@ use vello_common::color::palette::css::{BLACK, BLUE, WHITE, YELLOW}; use vello_common::color::{ColorSpaceTag, DynamicColor}; use vello_common::kurbo::{Point, Rect}; -use vello_common::paint::Gradient; -use vello_common::peniko::{ColorStop, ColorStops, GradientKind}; +use vello_common::peniko::{ColorStop, ColorStops, Gradient, GradientKind}; pub(crate) const fn tan_45() -> f64 { 1.0 @@ -134,8 +133,7 @@ }; use std::f64::consts::PI; use vello_common::kurbo::{Affine, Point, Rect}; - use vello_common::paint::Gradient; - use vello_common::peniko::GradientKind; + use vello_common::peniko::{Gradient, GradientKind}; #[test] fn gradient_linear_2_stops() { @@ -541,8 +539,7 @@ }; use std::f64::consts::PI; use vello_common::kurbo::{Affine, Point, Rect}; - use vello_common::paint::Gradient; - use vello_common::peniko::GradientKind::Radial; + use vello_common::peniko::{Gradient, GradientKind::Radial}; macro_rules! simple { ($stops:expr, $name:expr) => { @@ -958,8 +955,7 @@ }; use std::f64::consts::PI; use vello_common::kurbo::{Affine, Point, Rect}; - use vello_common::paint::Gradient; - use vello_common::peniko::GradientKind; + use vello_common::peniko::{Gradient, GradientKind}; macro_rules! basic { ($stops:expr, $name:expr, $center:expr) => {
diff --git a/sparse_strips/vello_cpu/tests/image.rs b/sparse_strips/vello_cpu/tests/image.rs index b2f0c2f..1959e46 100644 --- a/sparse_strips/vello_cpu/tests/image.rs +++ b/sparse_strips/vello_cpu/tests/image.rs
@@ -51,8 +51,8 @@ x_extend: $x_repeat, y_extend: $y_repeat, quality: ImageQuality::Low, - transform: Affine::translate((45.0, 45.0)), }); + ctx.set_paint_transform(Affine::translate((45.0, 45.0))); ctx.fill_rect(&rect); check_ref(&ctx, $name); @@ -98,7 +98,6 @@ x_extend: Extend::Repeat, y_extend: Extend::Repeat, quality: ImageQuality::Low, - transform: Affine::IDENTITY, }; ctx.set_transform($transform); @@ -270,7 +269,6 @@ x_extend: Extend::Repeat, y_extend: Extend::Repeat, quality: ImageQuality::Low, - transform: Affine::IDENTITY, }; ctx.set_paint(image); @@ -292,7 +290,6 @@ x_extend: Extend::Repeat, y_extend: Extend::Repeat, quality: ImageQuality::Low, - transform: Affine::IDENTITY, }; ctx.set_paint(image); @@ -311,7 +308,6 @@ x_extend: Extend::Repeat, y_extend: Extend::Repeat, quality: ImageQuality::Low, - transform: Affine::IDENTITY, }; ctx.set_paint(image); @@ -351,10 +347,10 @@ x_extend: $extend, y_extend: $extend, quality: $quality, - transform: $transform, }; ctx.set_paint(image); + ctx.set_paint_transform($transform); ctx.fill_rect(&rect); check_ref(&ctx, $name);
diff --git a/sparse_strips/vello_cpu/tests/mask.rs b/sparse_strips/vello_cpu/tests/mask.rs index 4503c70..5b76b23 100644 --- a/sparse_strips/vello_cpu/tests/mask.rs +++ b/sparse_strips/vello_cpu/tests/mask.rs
@@ -7,8 +7,7 @@ use vello_common::color::palette::css::{BLACK, LIME, RED, YELLOW}; use vello_common::kurbo::{Point, Rect}; use vello_common::mask::Mask; -use vello_common::paint::Gradient; -use vello_common::peniko::{ColorStop, ColorStops, GradientKind}; +use vello_common::peniko::{ColorStop, ColorStops, Gradient, GradientKind}; use vello_cpu::{Pixmap, RenderContext}; pub(crate) fn example_mask(alpha_mask: bool) -> Mask {
diff --git a/sparse_strips/vello_cpu/tests/mix.rs b/sparse_strips/vello_cpu/tests/mix.rs index f34e13c..5f8f3ba 100644 --- a/sparse_strips/vello_cpu/tests/mix.rs +++ b/sparse_strips/vello_cpu/tests/mix.rs
@@ -7,8 +7,8 @@ use vello_common::color::palette::css::{BLUE, LIME, MAGENTA, RED, YELLOW}; use vello_common::color::{AlphaColor, DynamicColor, Srgb}; use vello_common::kurbo::{Affine, Point, Rect}; -use vello_common::paint::{Gradient, Image}; -use vello_common::peniko::{BlendMode, Compose, Extend, Mix}; +use vello_common::paint::Image; +use vello_common::peniko::{BlendMode, Compose, Extend, Gradient, Mix}; use vello_common::peniko::{ColorStop, ColorStops, GradientKind, ImageQuality}; // The outputs have been compared visually with tiny-skia, and except for two cases (where tiny-skia @@ -52,7 +52,6 @@ x_extend: Extend::Pad, y_extend: Extend::Pad, quality: ImageQuality::Low, - transform: Affine::IDENTITY, }; ctx.set_transform(Affine::translate((10.0, 10.0)));