Skip to content

Commit bf4962a

Browse files
authored
sparse_strips: Use peniko::Brush (#1253)
demo for linebender/peniko#134 --------- Signed-off-by: sagudev <[email protected]>
1 parent b7aac65 commit bf4962a

File tree

13 files changed

+233
-200
lines changed

13 files changed

+233
-200
lines changed

sparse_strips/vello_bench/src/fine/image.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use vello_common::kurbo::Affine;
1111
use vello_common::paint::{Image, ImageSource};
1212
use vello_common::peniko;
1313
use vello_common::peniko::ImageQuality;
14+
use vello_common::peniko::ImageSampler;
1415
use vello_common::pixmap::Pixmap;
1516
use vello_cpu::fine::{Fine, FineKernel};
1617

@@ -148,10 +149,13 @@ fn get_colr_image(extend: peniko::Extend, quality: ImageQuality) -> Image {
148149

149150
let pixmap = Pixmap::from_png(&data[..]).unwrap();
150151
Image {
151-
source: ImageSource::Pixmap(Arc::new(pixmap)),
152-
x_extend: extend,
153-
y_extend: extend,
154-
quality,
152+
image: ImageSource::Pixmap(Arc::new(pixmap)),
153+
sampler: ImageSampler {
154+
x_extend: extend,
155+
y_extend: extend,
156+
quality,
157+
alpha: 1.0,
158+
},
155159
}
156160
}
157161

@@ -160,10 +164,13 @@ fn get_small_image(extend: peniko::Extend, quality: ImageQuality) -> Image {
160164

161165
let pixmap = Pixmap::from_png(&data[..]).unwrap();
162166
Image {
163-
source: ImageSource::Pixmap(Arc::new(pixmap)),
164-
x_extend: extend,
165-
y_extend: extend,
166-
quality,
167+
image: ImageSource::Pixmap(Arc::new(pixmap)),
168+
sampler: ImageSampler {
169+
x_extend: extend,
170+
y_extend: extend,
171+
quality,
172+
alpha: 1.0,
173+
},
167174
}
168175
}
169176

sparse_strips/vello_common/src/encode.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use core::hash::{Hash, Hasher};
2020
use fearless_simd::{Simd, SimdBase, SimdFloat, f32x4, f32x16};
2121
use peniko::color::cache_key::{BitEq, BitHash, CacheKey};
2222
use peniko::{
23-
InterpolationAlphaSpace, LinearGradientPosition, RadialGradientPosition, SweepGradientPosition,
23+
ImageSampler, InterpolationAlphaSpace, LinearGradientPosition, RadialGradientPosition,
24+
SweepGradientPosition,
2425
};
2526
use smallvec::ToSmallVec;
2627
// So we can just use `OnceCell` regardless of which feature is activated.
@@ -430,7 +431,12 @@ impl EncodeExt for Image {
430431
fn encode_into(&self, paints: &mut Vec<EncodedPaint>, transform: Affine) -> Paint {
431432
let idx = paints.len();
432433

433-
let mut quality = self.quality;
434+
let mut sampler = self.sampler;
435+
436+
if sampler.alpha != 1.0 {
437+
// If the sampler alpha is not 1.0, we need to force alpha compositing.
438+
unimplemented!("Applying opacity to image commands");
439+
}
434440

435441
let c = transform.as_coeffs();
436442

@@ -441,9 +447,9 @@ impl EncodeExt for Image {
441447
&& (c[3] as f32 - 1.0).is_nearly_zero()
442448
&& ((c[4] - c[4].floor()) as f32).is_nearly_zero()
443449
&& ((c[5] - c[5].floor()) as f32).is_nearly_zero()
444-
&& quality == ImageQuality::Medium
450+
&& sampler.quality == ImageQuality::Medium
445451
{
446-
quality = ImageQuality::Low;
452+
sampler.quality = ImageQuality::Low;
447453
}
448454

449455
// Similarly to gradients, apply a 0.5 offset so we sample at the center of
@@ -452,12 +458,11 @@ impl EncodeExt for Image {
452458

453459
let (x_advance, y_advance) = x_y_advances(&transform);
454460

455-
let encoded = match &self.source {
461+
let encoded = match &self.image {
456462
ImageSource::Pixmap(pixmap) => {
457463
EncodedImage {
458464
source: ImageSource::Pixmap(pixmap.clone()),
459-
extends: (self.x_extend, self.y_extend),
460-
quality,
465+
sampler,
461466
// While we could optimize RGB8 images, it's probably not worth the trouble.
462467
has_opacities: true,
463468
transform,
@@ -467,8 +472,7 @@ impl EncodeExt for Image {
467472
}
468473
ImageSource::OpaqueId(image) => EncodedImage {
469474
source: ImageSource::OpaqueId(*image),
470-
extends: (self.x_extend, self.y_extend),
471-
quality: self.quality,
475+
sampler,
472476
has_opacities: true,
473477
transform,
474478
x_advance,
@@ -510,10 +514,8 @@ impl From<EncodedBlurredRoundedRectangle> for EncodedPaint {
510514
pub struct EncodedImage {
511515
/// The underlying pixmap of the image.
512516
pub source: ImageSource,
513-
/// The extends in the horizontal and vertical direction.
514-
pub extends: (Extend, Extend),
515-
/// The rendering quality of the image.
516-
pub quality: ImageQuality,
517+
/// Sampler
518+
pub sampler: ImageSampler,
517519
/// Whether the image has opacities.
518520
pub has_opacities: bool,
519521
/// A transform to apply to the image.

sparse_strips/vello_common/src/paint.rs

Lines changed: 36 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use crate::pixmap::Pixmap;
77
use alloc::sync::Arc;
88
use peniko::{
9-
Gradient, ImageQuality, ImageSampler,
9+
Gradient,
1010
color::{AlphaColor, PremulRgba8, Srgb},
1111
};
1212

@@ -77,84 +77,68 @@ pub enum ImageSource {
7777
OpaqueId(ImageId),
7878
}
7979

80-
/// An image.
81-
#[derive(Debug, Clone)]
82-
pub struct Image {
83-
/// The underlying pixmap of the image.
84-
pub source: ImageSource,
85-
/// Extend mode in the horizontal direction.
86-
pub x_extend: peniko::Extend,
87-
/// Extend mode in the vertical direction.
88-
pub y_extend: peniko::Extend,
89-
/// Hint for desired rendering quality.
90-
pub quality: ImageQuality,
91-
}
92-
93-
impl Image {
94-
/// Convert a [`peniko::ImageBrush`] to an [`Image`].
80+
impl ImageSource {
81+
/// Convert a [`peniko::ImageData`] to an [`ImageSource`].
9582
///
9683
/// This is a somewhat lossy conversion, as the image data data is transformed to
9784
/// [premultiplied RGBA8](`PremulRgba8`).
9885
///
9986
/// # Panics
10087
///
10188
/// This panics if `image` has a `width` or `height` greater than `u16::MAX`.
102-
pub fn from_peniko_image(brush: &peniko::ImageBrush) -> Self {
89+
pub fn from_peniko_image_data(image: &peniko::ImageData) -> Self {
10390
// TODO: how do we deal with `peniko::ImageFormat` growing? See also
10491
// <https://github.com/linebender/vello/pull/996#discussion_r2080510863>.
105-
if brush.image.format != peniko::ImageFormat::Rgba8 {
106-
unimplemented!("Unsupported image format: {:?}", brush.image.format);
107-
}
108-
if brush.image.alpha_type != peniko::ImageAlphaType::Alpha {
109-
unimplemented!("Unsupported image alpha type: {:?}", brush.image.alpha_type);
110-
}
92+
let do_alpha_multiply = image.alpha_type != peniko::ImageAlphaType::AlphaPremultiplied;
11193

11294
assert!(
113-
brush.image.width <= u16::MAX as u32 && brush.image.height <= u16::MAX as u32,
95+
image.width <= u16::MAX as u32 && image.height <= u16::MAX as u32,
11496
"The image is too big. Its width and height can be no larger than {} pixels.",
11597
u16::MAX,
11698
);
117-
let width = brush.image.width.try_into().unwrap();
118-
let height = brush.image.height.try_into().unwrap();
119-
let ImageSampler {
120-
x_extend,
121-
y_extend,
122-
quality,
123-
alpha: global_alpha,
124-
} = brush.sampler;
125-
126-
#[expect(clippy::cast_possible_truncation, reason = "deliberate quantization")]
127-
let global_alpha = u16::from((global_alpha * 255. + 0.5) as u8);
99+
let width = image.width.try_into().unwrap();
100+
let height = image.height.try_into().unwrap();
128101

129102
// TODO: SIMD
130103
#[expect(clippy::cast_possible_truncation, reason = "This cannot overflow.")]
131-
let pixels = brush
132-
.image
104+
let pixels = image
133105
.data
134106
.data()
135107
.chunks_exact(4)
136-
.map(|rgba| {
137-
let alpha = ((u16::from(rgba[3]) * global_alpha) / 255) as u8;
138-
let multiply = |component| ((u16::from(alpha) * u16::from(component)) / 255) as u8;
139-
PremulRgba8 {
140-
r: multiply(rgba[0]),
141-
g: multiply(rgba[1]),
142-
b: multiply(rgba[2]),
143-
a: alpha,
108+
.map(|pixel| {
109+
let rgba: [u8; 4] = match image.format {
110+
peniko::ImageFormat::Rgba8 => pixel.try_into().unwrap(),
111+
peniko::ImageFormat::Bgra8 => [pixel[2], pixel[1], pixel[0], pixel[3]],
112+
format => unimplemented!("Unsupported image format: {format:?}"),
113+
};
114+
let alpha = u16::from(rgba[3]);
115+
let multiply = |component| ((alpha * u16::from(component)) / 255) as u8;
116+
if do_alpha_multiply {
117+
PremulRgba8 {
118+
r: multiply(rgba[0]),
119+
g: multiply(rgba[1]),
120+
b: multiply(rgba[2]),
121+
a: rgba[3],
122+
}
123+
} else {
124+
PremulRgba8 {
125+
r: rgba[0],
126+
g: rgba[1],
127+
b: rgba[2],
128+
a: rgba[3],
129+
}
144130
}
145131
})
146132
.collect();
147133
let pixmap = Pixmap::from_parts(pixels, width, height);
148134

149-
Self {
150-
source: ImageSource::Pixmap(Arc::new(pixmap)),
151-
x_extend,
152-
y_extend,
153-
quality,
154-
}
135+
Self::Pixmap(Arc::new(pixmap))
155136
}
156137
}
157138

139+
/// An image.
140+
pub type Image = peniko::ImageBrush<ImageSource>;
141+
158142
/// A premultiplied color.
159143
#[derive(Debug, Clone, PartialEq, Copy)]
160144
pub struct PremulColor {
@@ -193,30 +177,4 @@ impl PremulColor {
193177
}
194178

195179
/// A kind of paint that can be used for filling and stroking shapes.
196-
#[derive(Debug, Clone)]
197-
pub enum PaintType {
198-
/// A solid color.
199-
Solid(AlphaColor<Srgb>),
200-
/// A gradient.
201-
Gradient(Gradient),
202-
/// An image.
203-
Image(Image),
204-
}
205-
206-
impl From<AlphaColor<Srgb>> for PaintType {
207-
fn from(value: AlphaColor<Srgb>) -> Self {
208-
Self::Solid(value)
209-
}
210-
}
211-
212-
impl From<Gradient> for PaintType {
213-
fn from(value: Gradient) -> Self {
214-
Self::Gradient(value)
215-
}
216-
}
217-
218-
impl From<Image> for PaintType {
219-
fn from(value: Image) -> Self {
220-
Self::Image(value)
221-
}
222-
}
180+
pub type PaintType = peniko::Brush<Image, Gradient>;

sparse_strips/vello_cpu/src/fine/common/image.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'a, S: Simd> PlainNNImagePainter<'a, S> {
3838
data.x_advances.1,
3939
data.y_advances.1,
4040
),
41-
image.extends.1,
41+
image.sampler.y_extend,
4242
data.height,
4343
data.height_inv,
4444
);
@@ -68,7 +68,7 @@ impl<S: Simd> Iterator for PlainNNImagePainter<'_, S> {
6868
let x_pos = extend(
6969
self.simd,
7070
self.cur_x_pos,
71-
self.data.image.extends.0,
71+
self.data.image.sampler.x_extend,
7272
self.data.width,
7373
self.data.width_inv,
7474
);
@@ -116,7 +116,7 @@ impl<S: Simd> Iterator for NNImagePainter<'_, S> {
116116
self.data.x_advances.0,
117117
self.data.y_advances.0,
118118
),
119-
self.data.image.extends.0,
119+
self.data.image.sampler.x_extend,
120120
self.data.width,
121121
self.data.width_inv,
122122
);
@@ -129,7 +129,7 @@ impl<S: Simd> Iterator for NNImagePainter<'_, S> {
129129
self.data.x_advances.1,
130130
self.data.y_advances.1,
131131
),
132-
self.data.image.extends.1,
132+
self.data.image.sampler.y_extend,
133133
self.data.height,
134134
self.data.height_inv,
135135
);
@@ -214,7 +214,7 @@ impl<S: Simd> Iterator for FilteredImagePainter<'_, S> {
214214
extend(
215215
self.simd,
216216
x_positions + $offsets[$idx],
217-
self.data.image.extends.0,
217+
self.data.image.sampler.y_extend,
218218
self.data.width,
219219
self.data.width_inv,
220220
)
@@ -226,14 +226,14 @@ impl<S: Simd> Iterator for FilteredImagePainter<'_, S> {
226226
extend(
227227
self.simd,
228228
y_positions + $offsets[$idx],
229-
self.data.image.extends.1,
229+
self.data.image.sampler.y_extend,
230230
self.data.height,
231231
self.data.height_inv,
232232
)
233233
};
234234
}
235235

236-
match self.data.image.quality {
236+
match self.data.image.sampler.quality {
237237
ImageQuality::Low => unreachable!(),
238238
ImageQuality::Medium => {
239239
// <https://github.com/google/skia/blob/220738774f7a0ce4a6c7bd17519a336e5e5dea5b/src/opts/SkRasterPipeline_opts.h#L5039-L5078>

sparse_strips/vello_cpu/src/fine/lowp/image.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl<S: Simd> Iterator for BilinearImagePainter<'_, S> {
6060
extend(
6161
self.simd,
6262
x_pos,
63-
self.data.image.extends.0,
63+
self.data.image.sampler.x_extend,
6464
self.data.width,
6565
self.data.width_inv,
6666
)
@@ -70,7 +70,7 @@ impl<S: Simd> Iterator for BilinearImagePainter<'_, S> {
7070
extend(
7171
self.simd,
7272
y_pos,
73-
self.data.image.extends.1,
73+
self.data.image.sampler.y_extend,
7474
self.data.height,
7575
self.data.height_inv,
7676
)

sparse_strips/vello_cpu/src/fine/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ impl<S: Simd, T: FineKernel<S>> Fine<S, T> {
573573

574574
match (i.has_skew(), i.nearest_neighbor()) {
575575
(_, false) => {
576-
if i.quality == ImageQuality::Medium {
576+
if i.sampler.quality == ImageQuality::Medium {
577577
fill_complex_paint!(
578578
i.has_opacities,
579579
T::medium_quality_image_painter(

0 commit comments

Comments
 (0)