Skip to content

Commit 80fc8a1

Browse files
committed
Add a draw_pixmap method
1 parent 462fd4e commit 80fc8a1

File tree

8 files changed

+220
-76
lines changed

8 files changed

+220
-76
lines changed

sparse_strips/vello_common/src/encode.rs

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use crate::color::palette::css::BLACK;
88
use crate::color::{ColorSpaceTag, HueDirection, Srgb, gradient};
99
use crate::kurbo::{Affine, Point, Vec2};
1010
use crate::math::{FloatExt, compute_erf7};
11-
use crate::paint::{Image, ImageSource, IndexedPaint, Paint, PremulColor};
12-
use crate::peniko::{ColorStop, ColorStops, Extend, Gradient, GradientKind, ImageQuality};
11+
use crate::paint::{Image, ImageSource, IndexedPaint, Paint, PremulColor, encode_image_with};
12+
use crate::peniko::{ColorStop, ColorStops, Extend, Gradient, GradientKind};
1313
use alloc::borrow::Cow;
1414
use alloc::fmt::Debug;
1515
use alloc::vec;
@@ -429,60 +429,7 @@ impl private::Sealed for Image {}
429429

430430
impl EncodeExt for Image {
431431
fn encode_into(&self, paints: &mut Vec<EncodedPaint>, transform: Affine) -> Paint {
432-
let idx = paints.len();
433-
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-
}
440-
441-
let c = transform.as_coeffs();
442-
443-
// Optimize image quality for integer-only translations.
444-
if (c[0] as f32 - 1.0).is_nearly_zero()
445-
&& (c[1] as f32).is_nearly_zero()
446-
&& (c[2] as f32).is_nearly_zero()
447-
&& (c[3] as f32 - 1.0).is_nearly_zero()
448-
&& ((c[4] - c[4].floor()) as f32).is_nearly_zero()
449-
&& ((c[5] - c[5].floor()) as f32).is_nearly_zero()
450-
&& sampler.quality == ImageQuality::Medium
451-
{
452-
sampler.quality = ImageQuality::Low;
453-
}
454-
455-
// Similarly to gradients, apply a 0.5 offset so we sample at the center of
456-
// a pixel.
457-
let transform = transform.inverse() * Affine::translate((0.5, 0.5));
458-
459-
let (x_advance, y_advance) = x_y_advances(&transform);
460-
461-
let encoded = match &self.image {
462-
ImageSource::Pixmap(pixmap) => {
463-
EncodedImage {
464-
source: ImageSource::Pixmap(pixmap.clone()),
465-
sampler,
466-
// While we could optimize RGB8 images, it's probably not worth the trouble.
467-
has_opacities: true,
468-
transform,
469-
x_advance,
470-
y_advance,
471-
}
472-
}
473-
ImageSource::OpaqueId(image) => EncodedImage {
474-
source: ImageSource::OpaqueId(*image),
475-
sampler,
476-
has_opacities: true,
477-
transform,
478-
x_advance,
479-
y_advance,
480-
},
481-
};
482-
483-
paints.push(EncodedPaint::Image(encoded));
484-
485-
Paint::Indexed(IndexedPaint::new(idx))
432+
encode_image_with(self, paints, transform, false)
486433
}
487434
}
488435

@@ -524,6 +471,30 @@ pub struct EncodedImage {
524471
pub x_advance: Vec2,
525472
/// The advance in image coordinates for one step in the y direction.
526473
pub y_advance: Vec2,
474+
/// Whether the extends in `ImageSampler` should be ignored.
475+
pub ignore_extend: bool,
476+
}
477+
478+
impl EncodedImage {
479+
/// Return the x extend of the image.
480+
#[inline(always)]
481+
pub fn x_extend(&self) -> Option<peniko::Extend> {
482+
if self.ignore_extend {
483+
None
484+
} else {
485+
Some(self.sampler.x_extend)
486+
}
487+
}
488+
489+
/// Return the y extend of the image.
490+
#[inline(always)]
491+
pub fn y_extend(&self) -> Option<peniko::Extend> {
492+
if self.ignore_extend {
493+
None
494+
} else {
495+
Some(self.sampler.y_extend)
496+
}
497+
}
527498
}
528499

529500
/// Computed properties of a linear gradient.

sparse_strips/vello_common/src/paint.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@
33

44
//! Types for paints.
55
6+
use crate::encode::{EncodedImage, EncodedPaint, x_y_advances};
7+
use crate::kurbo::Affine;
8+
use crate::math::FloatExt;
69
use crate::pixmap::Pixmap;
710
use alloc::sync::Arc;
11+
use alloc::vec::Vec;
812
use peniko::{
9-
Gradient,
13+
Gradient, ImageQuality,
1014
color::{AlphaColor, PremulRgba8, Srgb},
1115
};
1216

17+
#[cfg(not(feature = "std"))]
18+
use peniko::kurbo::common::FloatFuncs as _;
19+
1320
/// A paint that needs to be resolved via its index.
1421
// In the future, we might add additional flags, that's why we have
1522
// this thin wrapper around u32, so we can change the underlying
@@ -139,6 +146,71 @@ impl ImageSource {
139146
/// An image.
140147
pub type Image = peniko::ImageBrush<ImageSource>;
141148

149+
/// Encode the image, optionally ignoring the extends of the image.
150+
pub fn encode_image_with(
151+
image: &Image,
152+
paints: &mut Vec<EncodedPaint>,
153+
transform: Affine,
154+
ignore_extend: bool,
155+
) -> Paint {
156+
let idx = paints.len();
157+
158+
let mut sampler = image.sampler;
159+
160+
if sampler.alpha != 1.0 {
161+
// If the sampler alpha is not 1.0, we need to force alpha compositing.
162+
unimplemented!("Applying opacity to image commands");
163+
}
164+
165+
let c = transform.as_coeffs();
166+
167+
// Optimize image quality for integer-only translations.
168+
if (c[0] as f32 - 1.0).is_nearly_zero()
169+
&& (c[1] as f32).is_nearly_zero()
170+
&& (c[2] as f32).is_nearly_zero()
171+
&& (c[3] as f32 - 1.0).is_nearly_zero()
172+
&& ((c[4] - c[4].floor()) as f32).is_nearly_zero()
173+
&& ((c[5] - c[5].floor()) as f32).is_nearly_zero()
174+
&& sampler.quality == ImageQuality::Medium
175+
{
176+
sampler.quality = ImageQuality::Low;
177+
}
178+
179+
// Similarly to gradients, apply a 0.5 offset so we sample at the center of
180+
// a pixel.
181+
let transform = transform.inverse() * Affine::translate((0.5, 0.5));
182+
183+
let (x_advance, y_advance) = x_y_advances(&transform);
184+
185+
let encoded = match &image.image {
186+
ImageSource::Pixmap(pixmap) => {
187+
EncodedImage {
188+
source: ImageSource::Pixmap(pixmap.clone()),
189+
sampler,
190+
// While we could optimize RGB8 images, it's probably not worth the trouble.
191+
has_opacities: true,
192+
transform,
193+
x_advance,
194+
y_advance,
195+
ignore_extend,
196+
}
197+
}
198+
ImageSource::OpaqueId(image) => EncodedImage {
199+
source: ImageSource::OpaqueId(*image),
200+
sampler,
201+
has_opacities: true,
202+
transform,
203+
x_advance,
204+
y_advance,
205+
ignore_extend,
206+
},
207+
};
208+
209+
paints.push(EncodedPaint::Image(encoded));
210+
211+
Paint::Indexed(IndexedPaint::new(idx))
212+
}
213+
142214
/// A premultiplied color.
143215
#[derive(Debug, Clone, PartialEq, Copy)]
144216
pub struct PremulColor {

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

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use vello_common::fearless_simd::{Bytes, Simd, SimdBase, SimdFloat, f32x4, f32x1
1010
use vello_common::pixmap::Pixmap;
1111
use vello_common::simd::element_wise_splat;
1212

13+
const PIXEL_OOB_MARKER: f32 = -1.0;
14+
1315
/// A painter for nearest-neighbor images with no skewing.
1416
#[derive(Debug)]
1517
pub(crate) struct PlainNNImagePainter<'a, S: Simd> {
@@ -38,7 +40,7 @@ impl<'a, S: Simd> PlainNNImagePainter<'a, S> {
3840
data.x_advances.1,
3941
data.y_advances.1,
4042
),
41-
image.sampler.y_extend,
43+
image.y_extend(),
4244
data.height,
4345
data.height_inv,
4446
);
@@ -68,7 +70,7 @@ impl<S: Simd> Iterator for PlainNNImagePainter<'_, S> {
6870
let x_pos = extend(
6971
self.simd,
7072
self.cur_x_pos,
71-
self.data.image.sampler.x_extend,
73+
self.data.image.x_extend(),
7274
self.data.width,
7375
self.data.width_inv,
7476
);
@@ -116,7 +118,7 @@ impl<S: Simd> Iterator for NNImagePainter<'_, S> {
116118
self.data.x_advances.0,
117119
self.data.y_advances.0,
118120
),
119-
self.data.image.sampler.x_extend,
121+
self.data.image.x_extend(),
120122
self.data.width,
121123
self.data.width_inv,
122124
);
@@ -129,7 +131,7 @@ impl<S: Simd> Iterator for NNImagePainter<'_, S> {
129131
self.data.x_advances.1,
130132
self.data.y_advances.1,
131133
),
132-
self.data.image.sampler.y_extend,
134+
self.data.image.y_extend(),
133135
self.data.height,
134136
self.data.height_inv,
135137
);
@@ -214,7 +216,7 @@ impl<S: Simd> Iterator for FilteredImagePainter<'_, S> {
214216
extend(
215217
self.simd,
216218
x_positions + $offsets[$idx],
217-
self.data.image.sampler.y_extend,
219+
self.data.image.x_extend(),
218220
self.data.width,
219221
self.data.width_inv,
220222
)
@@ -226,7 +228,7 @@ impl<S: Simd> Iterator for FilteredImagePainter<'_, S> {
226228
extend(
227229
self.simd,
228230
y_positions + $offsets[$idx],
229-
self.data.image.sampler.y_extend,
231+
self.data.image.y_extend(),
230232
self.data.height,
231233
self.data.height_inv,
232234
)
@@ -335,6 +337,7 @@ pub(crate) struct ImagePainterData<'a, S: Simd> {
335337
pub(crate) pixmap: &'a Pixmap,
336338
pub(crate) x_advances: (f32, f32),
337339
pub(crate) y_advances: (f32, f32),
340+
pub(crate) mask_oob: bool,
338341
pub(crate) height: f32x4<S>,
339342
pub(crate) height_inv: f32x4<S>,
340343
pub(crate) width: f32x4<S>,
@@ -362,11 +365,13 @@ impl<'a, S: Simd> ImagePainterData<'a, S> {
362365

363366
let x_advances = (image.x_advance.x as f32, image.x_advance.y as f32);
364367
let y_advances = (image.y_advance.x as f32, image.y_advance.y as f32);
368+
let mask_oob = image.ignore_extend;
365369

366370
Self {
367371
cur_pos: start_pos,
368372
pixmap,
369373
x_advances,
374+
mask_oob,
370375
y_advances,
371376
image,
372377
width,
@@ -387,23 +392,33 @@ pub(crate) fn sample<S: Simd>(
387392
) -> u8x16<S> {
388393
let idx = x_positions.cvt_u32() + y_positions.cvt_u32() * data.width_u32;
389394

390-
u32x4::from_slice(
395+
let mut res = u32x4::from_slice(
391396
simd,
392397
&[
393398
data.pixmap.sample_idx(idx[0]).to_u32(),
394399
data.pixmap.sample_idx(idx[1]).to_u32(),
395400
data.pixmap.sample_idx(idx[2]).to_u32(),
396401
data.pixmap.sample_idx(idx[3]).to_u32(),
397402
],
398-
)
399-
.reinterpret_u8()
403+
);
404+
405+
if data.mask_oob {
406+
let mask = simd.or_mask32x4(
407+
x_positions.simd_eq(f32x4::splat(simd, PIXEL_OOB_MARKER)),
408+
y_positions.simd_eq(f32x4::splat(simd, PIXEL_OOB_MARKER)),
409+
);
410+
411+
res = simd.select_u32x4(mask, u32x4::splat(simd, 0), res);
412+
}
413+
414+
res.reinterpret_u8()
400415
}
401416

402417
#[inline(always)]
403418
pub(crate) fn extend<S: Simd>(
404419
simd: S,
405420
val: f32x4<S>,
406-
extend: crate::peniko::Extend,
421+
extend: Option<crate::peniko::Extend>,
407422
max: f32x4<S>,
408423
inv_max: f32x4<S>,
409424
) -> f32x4<S> {
@@ -414,13 +429,13 @@ pub(crate) fn extend<S: Simd>(
414429
match extend {
415430
// Note that max should be exclusive, so subtract a small bias to enforce that.
416431
// Otherwise, we might sample out-of-bounds pixels.
417-
crate::peniko::Extend::Pad => val.min(max - bias).max(f32x4::splat(simd, 0.0)),
418-
crate::peniko::Extend::Repeat => val
432+
Some(crate::peniko::Extend::Pad) => val.min(max - bias).max(f32x4::splat(simd, 0.0)),
433+
Some(crate::peniko::Extend::Repeat) => val
419434
.msub((val * inv_max).floor(), max)
420435
// In certain edge cases, we might still end up with a higher number.
421436
.min(max - 1.0),
422437
// <https://github.com/google/skia/blob/220738774f7a0ce4a6c7bd17519a336e5e5dea5b/src/opts/SkRasterPipeline_opts.h#L3274-L3290>
423-
crate::peniko::Extend::Reflect => {
438+
Some(crate::peniko::Extend::Reflect) => {
424439
let u = val
425440
- (val * inv_max * f32x4::splat(simd, 0.5)).floor() * f32x4::splat(simd, 2.0) * max;
426441
let s = (u * inv_max).floor();
@@ -438,6 +453,10 @@ pub(crate) fn extend<S: Simd>(
438453
// In certain edge cases, we might still end up with a higher number.
439454
.min(max - 1.0)
440455
}
456+
None => {
457+
let mask = simd.and_mask32x4(val.simd_ge(f32x4::splat(simd, 0.0)), val.simd_lt(max));
458+
simd.select_f32x4(mask, val, f32x4::splat(simd, PIXEL_OOB_MARKER))
459+
}
441460
}
442461
}
443462

@@ -546,7 +565,7 @@ mod tests {
546565
let max_inv = 1.0 / max;
547566

548567
let num = f32x4::splat(simd, 127.00001);
549-
let res = extend(simd, num, crate::peniko::Extend::Repeat, max, max_inv);
568+
let res = extend(simd, num, Some(crate::peniko::Extend::Repeat), max, max_inv);
550569

551570
assert!(res[0] <= 127.0);
552571
}

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.sampler.x_extend,
63+
self.data.image.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.sampler.y_extend,
73+
self.data.image.y_extend(),
7474
self.data.height,
7575
self.data.height_inv,
7676
)

0 commit comments

Comments
 (0)