vello_common: Unify and improve constructor for pixmaps (#1834)
This PR makes two changes:
- It combines all pixmap constructors into a single constructor that
always takes Vec<u8> and makes the specific alpha options an additional
configuration. This way we have only a single entry point and avoid
duplicate documentation.
- It provides a way of submitting unpremultiplied data directly, so that
clients don't have to reimplement it. It drastically improves the
performance of the premultiplication step (compared to what is currently
used for PNGs) by only relying on u8/u16 and using SIMD. A small
downside is that the precision can be slightly worse, hence why some
test images had to be regenerated. In the normal case there was no delta
larger than 1, except for the blending test cases where the rounding
errors while performing blending. If this is a problem, I'm happy to add
another parameter for defining whether to do premultiplication using
exact f32 arithmetic or not and try to SIMDify it as well, but I would
like to avoid this.
## Neon
On NEON, I'm getting the following results compared to what we had
before (in a make-shift benchmark I set up, comparing the existing
premultiplication code of the from_png method to the new one). As you
can see, it basically gives us a 3x speedup!
```
premultiply_rgba8/800x500/old_scalar
time: [156.82 µs 157.15 µs 157.48 µs]
thrpt: [9.4621 GiB/s 9.4821 GiB/s 9.5023 GiB/s]
premultiply_rgba8/800x500/new_simd_interleaved_64_with_transparency
time: [46.522 µs 46.593 µs 46.664 µs]
thrpt: [31.933 GiB/s 31.981 GiB/s 32.030 GiB/s]
premultiply_rgba8/1920x1080/old_scalar
time: [827.20 µs 828.84 µs 830.58 µs]
thrpt: [9.3005 GiB/s 9.3199 GiB/s 9.3385 GiB/s]
premultiply_rgba8/1920x1080/new_simd_interleaved_64_with_transparency
time: [243.32 µs 243.69 µs 244.03 µs]
thrpt: [31.655 GiB/s 31.699 GiB/s 31.748 GiB/s]
premultiply_rgba8/3840x2160/old_scalar
time: [3.3393 ms 3.3459 ms 3.3529 ms]
thrpt: [9.2155 GiB/s 9.2349 GiB/s 9.2532 GiB/s]
premultiply_rgba8/3840x2160/new_simd_interleaved_64_with_transparency
time: [980.54 µs 981.64 µs 982.72 µs]
thrpt: [31.442 GiB/s 31.477 GiB/s 31.512 GiB/s]
```
## AVX2
4x speedup!
```
premultiply_rgba8/800x500/old_scalar
time: [830.42 µs 836.86 µs 843.27 µs]
thrpt: [1.7671 GiB/s 1.7806 GiB/s 1.7944 GiB/s]
premultiply_rgba8/800x500/new_simd_interleaved_64_with_transparency
time: [194.31 µs 196.88 µs 199.57 µs]
thrpt: [7.4668 GiB/s 7.5687 GiB/s 7.6687 GiB/s]
premultiply_rgba8/1920x1080/old_scalar
time: [4.0676 ms 4.1098 ms 4.1722 ms]
thrpt: [1.8515 GiB/s 1.8796 GiB/s 1.8991 GiB/s]
premultiply_rgba8/1920x1080/new_simd_interleaved_64_with_transparency
time: [962.16 µs 964.40 µs 966.92 µs]
thrpt: [7.9891 GiB/s 8.0099 GiB/s 8.0285 GiB/s]
premultiply_rgba8/3840x2160/old_scalar
time: [16.177 ms 16.183 ms 16.190 ms]
thrpt: [1.9085 GiB/s 1.9094 GiB/s 1.9101 GiB/s]
premultiply_rgba8/3840x2160/new_simd_interleaved_64_with_transparency
time: [3.8700 ms 3.8794 ms 3.8898 ms]
thrpt: [7.9437 GiB/s 7.9649 GiB/s 7.9842 GiB/s]
```
## WASM
I also tested this on WASM on some devices on BrowserStack, and was
seeing similar results. Very weirdly, on an iPhone 12 I was even seeing
an 8x speedup!diff --git a/.typos.toml b/.typos.toml
index 64e9ec4..186436c 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -12,6 +12,7 @@
wdth = "wdth"
canva = "canva"
ArthurCose = "ArthurCose"
+ba = "ba"
# Match Inside a Word - Case Insensitive
[default.extend-words]
diff --git a/sparse_strips/vello_bench/benches/main.rs b/sparse_strips/vello_bench/benches/main.rs
index 13a031c..4e3a666 100644
--- a/sparse_strips/vello_bench/benches/main.rs
+++ b/sparse_strips/vello_bench/benches/main.rs
@@ -5,9 +5,10 @@
#![allow(dead_code, reason = "Might be unused on platforms not supporting SIMD")]
use criterion::{criterion_group, criterion_main};
-use vello_bench::{allocator, fine, flatten, glyph, integration, sort, strip, tile};
+use vello_bench::{allocator, fine, flatten, glyph, integration, pixmap, sort, strip, tile};
criterion_group!(allocator_bench, allocator::allocator);
+criterion_group!(pixmap_bench, pixmap::pixmap);
criterion_group!(fine_solid, fine::fill);
criterion_group!(fine_strip, fine::strip);
criterion_group!(fine_pack, fine::pack);
@@ -25,6 +26,7 @@
criterion_group!(sort_tiles, sort::sort);
criterion_group!(integration_bench, integration::images);
criterion_main!(
+ pixmap_bench,
allocator_bench,
tile,
render_strips,
diff --git a/sparse_strips/vello_bench/src/integration.rs b/sparse_strips/vello_bench/src/integration.rs
index 681a433..ad84d71 100644
--- a/sparse_strips/vello_bench/src/integration.rs
+++ b/sparse_strips/vello_bench/src/integration.rs
@@ -9,9 +9,8 @@
use vello_common::kurbo::{Affine, Rect};
use vello_common::paint::{Image, ImageSource};
use vello_common::peniko::ImageSampler;
-use vello_common::peniko::{Extend, ImageQuality};
-use vello_common::pixmap::Pixmap;
-use vello_cpu::color::AlphaColor;
+use vello_common::peniko::{Extend, ImageAlphaType, ImageQuality};
+use vello_common::pixmap::{PixelMetadata, Pixmap};
use vello_cpu::{RenderContext, Resources};
/// Image scene rendering benchmark.
@@ -72,27 +71,15 @@
let height = image.height();
let rgba_data = image.into_rgba8().into_vec();
- let mut may_have_transparency = false;
#[expect(
clippy::cast_possible_truncation,
reason = "Image dimensions fit in u16"
)]
- let pixmap = Pixmap::from_parts_with_opacity(
- rgba_data
- .chunks_exact(4)
- .map(|rgba| {
- let alpha = rgba[3];
- if alpha != 255 {
- may_have_transparency = true;
- }
- AlphaColor::from_rgba8(rgba[0], rgba[1], rgba[2], alpha)
- .premultiply()
- .to_rgba8()
- })
- .collect(),
+ let pixmap = Pixmap::from_parts(
+ rgba_data,
width as u16,
height as u16,
- may_have_transparency,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
);
ImageSource::Pixmap(Arc::new(pixmap))
diff --git a/sparse_strips/vello_bench/src/lib.rs b/sparse_strips/vello_bench/src/lib.rs
index e173d9d..5b2154f 100644
--- a/sparse_strips/vello_bench/src/lib.rs
+++ b/sparse_strips/vello_bench/src/lib.rs
@@ -13,6 +13,7 @@
pub mod flatten;
pub mod glyph;
pub mod integration;
+pub mod pixmap;
pub mod sort;
pub mod strip;
pub mod tile;
diff --git a/sparse_strips/vello_bench/src/pixmap.rs b/sparse_strips/vello_bench/src/pixmap.rs
new file mode 100644
index 0000000..d48ff60
--- /dev/null
+++ b/sparse_strips/vello_bench/src/pixmap.rs
@@ -0,0 +1,36 @@
+// Copyright 2026 the Vello Authors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use criterion::{BatchSize, Criterion, black_box};
+use vello_common::peniko::ImageAlphaType;
+use vello_common::pixmap::{PixelMetadata, Pixmap};
+
+const WIDTH: u16 = 1920;
+const HEIGHT: u16 = 1080;
+
+pub fn pixmap(c: &mut Criterion) {
+ let pixel_count = usize::from(WIDTH) * usize::from(HEIGHT);
+ let mut rgba = Vec::with_capacity(pixel_count * 4);
+ for index in 0..pixel_count {
+ let value = index.to_le_bytes()[0];
+ let alpha = if index.is_multiple_of(8) { 128 } else { 255 };
+ rgba.extend_from_slice(&[value, 255 - value, value / 2, alpha]);
+ }
+
+ let mut group = c.benchmark_group("pixmap");
+ group.bench_function("new", |b| {
+ b.iter_batched(
+ || rgba.clone(),
+ |rgba| {
+ black_box(Pixmap::from_parts(
+ rgba,
+ WIDTH,
+ HEIGHT,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
+ ));
+ },
+ BatchSize::LargeInput,
+ );
+ });
+ group.finish();
+}
diff --git a/sparse_strips/vello_common/Cargo.toml b/sparse_strips/vello_common/Cargo.toml
index e7e7314..327e731 100644
--- a/sparse_strips/vello_common/Cargo.toml
+++ b/sparse_strips/vello_common/Cargo.toml
@@ -17,7 +17,7 @@
targets = []
[dependencies]
-bytemuck = { workspace = true, features = ["derive"] }
+bytemuck = { workspace = true, features = ["derive", "extern_crate_alloc"] }
peniko = { workspace = true, features = ["bytemuck"] }
fearless_simd = { workspace = true }
png = { workspace = true, optional = true }
diff --git a/sparse_strips/vello_common/src/paint.rs b/sparse_strips/vello_common/src/paint.rs
index 62edee1..042697d 100644
--- a/sparse_strips/vello_common/src/paint.rs
+++ b/sparse_strips/vello_common/src/paint.rs
@@ -5,7 +5,7 @@
use crate::TextureId;
use crate::geometry::RectU16;
-use crate::pixmap::Pixmap;
+use crate::pixmap::{PixelMetadata, Pixmap};
use alloc::sync::Arc;
pub use peniko::Color;
use peniko::{
@@ -164,8 +164,6 @@
pub fn from_peniko_image_data(image: &peniko::ImageData) -> Self {
// TODO: how do we deal with `peniko::ImageFormat` growing? See also
// <https://github.com/linebender/vello/pull/996#discussion_r2080510863>.
- let do_alpha_multiply = image.alpha_type != peniko::ImageAlphaType::AlphaPremultiplied;
-
assert!(
image.width <= u16::MAX as u32 && image.height <= u16::MAX as u32,
"The image is too big. Its width and height can be no larger than {} pixels.",
@@ -174,40 +172,27 @@
let width = image.width.try_into().unwrap();
let height = image.height.try_into().unwrap();
- // TODO: SIMD
- let mut may_have_transparency = false;
- #[expect(clippy::cast_possible_truncation, reason = "This cannot overflow.")]
- let pixels = image
- .data
- .data()
- .chunks_exact(4)
- .map(|pixel| {
- let rgba: [u8; 4] = match image.format {
- peniko::ImageFormat::Rgba8 => pixel.try_into().unwrap(),
- peniko::ImageFormat::Bgra8 => [pixel[2], pixel[1], pixel[0], pixel[3]],
- format => unimplemented!("Unsupported image format: {format:?}"),
- };
- may_have_transparency |= rgba[3] != 255;
- let alpha = u16::from(rgba[3]);
- let multiply = |component| ((alpha * u16::from(component)) / 255) as u8;
- if do_alpha_multiply {
- PremulRgba8 {
- r: multiply(rgba[0]),
- g: multiply(rgba[1]),
- b: multiply(rgba[2]),
- a: rgba[3],
- }
- } else {
- PremulRgba8 {
- r: rgba[0],
- g: rgba[1],
- b: rgba[2],
- a: rgba[3],
- }
+ // Unfortunately, we have to create a new allocation, because pixmap requires
+ // a real vector.
+ // TODO: Figure out a better story for this.
+ let mut rgba = image.data.data().to_vec();
+ match image.format {
+ peniko::ImageFormat::Rgba8 => {}
+ peniko::ImageFormat::Bgra8 => {
+ // TODO: SIMDify
+ for pixel in rgba.chunks_exact_mut(4) {
+ pixel.swap(0, 2);
}
- })
- .collect();
- let pixmap = Pixmap::from_parts_with_opacity(pixels, width, height, may_have_transparency);
+ }
+ format => unimplemented!("Unsupported image format: {format:?}"),
+ }
+
+ let pixmap = Pixmap::from_parts(
+ rgba,
+ width,
+ height,
+ PixelMetadata::new(image.alpha_type, true),
+ );
Self::Pixmap(Arc::new(pixmap))
}
@@ -332,15 +317,22 @@
#[test]
fn from_peniko_image_data_computes_transparency_hint() {
- for alpha_type in [
+ let opaque = image_data(
+ &[10, 20, 30, 255, 40, 50, 60, 255],
peniko::ImageAlphaType::Alpha,
- peniko::ImageAlphaType::AlphaPremultiplied,
- ] {
- let opaque = image_data(&[10, 20, 30, 255, 40, 50, 60, 255], alpha_type);
- assert!(!ImageSource::from_peniko_image_data(&opaque).may_have_transparency());
+ );
+ assert!(!ImageSource::from_peniko_image_data(&opaque).may_have_transparency());
- let translucent = image_data(&[10, 20, 30, 255, 40, 50, 60, 128], alpha_type);
- assert!(ImageSource::from_peniko_image_data(&translucent).may_have_transparency());
- }
+ let translucent = image_data(
+ &[10, 20, 30, 255, 40, 50, 60, 128],
+ peniko::ImageAlphaType::Alpha,
+ );
+ assert!(ImageSource::from_peniko_image_data(&translucent).may_have_transparency());
+
+ let premultiplied = image_data(
+ &[10, 20, 30, 255, 40, 50, 60, 255],
+ peniko::ImageAlphaType::AlphaPremultiplied,
+ );
+ assert!(ImageSource::from_peniko_image_data(&premultiplied).may_have_transparency());
}
}
diff --git a/sparse_strips/vello_common/src/pixmap.rs b/sparse_strips/vello_common/src/pixmap.rs
index 70d1401..ae65391 100644
--- a/sparse_strips/vello_common/src/pixmap.rs
+++ b/sparse_strips/vello_common/src/pixmap.rs
@@ -8,7 +8,12 @@
#[cfg(feature = "png")]
use std::io::{BufRead, Seek};
-use crate::peniko::color::{PremulRgba8, Rgba8};
+use crate::fearless_simd::{Level, Simd, SimdBase, SimdInt, SimdMask, dispatch, mask8x16};
+use crate::peniko::{
+ ImageAlphaType,
+ color::{PremulRgba8, Rgba8},
+};
+use crate::util::Div255Ext;
#[cfg(feature = "png")]
extern crate std;
@@ -88,46 +93,42 @@
}
}
- /// Create a new pixmap with the given premultiplied RGBA8 data.
- ///
- /// The `data` vector must be of length `width * height` exactly.
- ///
- /// The pixels are in row-major order.
- ///
- /// This assumes the image may have transparent pixels. Use
- /// [`from_parts_with_opacity`](Self::from_parts_with_opacity) if you already
- /// know the opacity status to enable optimizations.
+ /// Create a new pixmap from the given buffer of bytes, representing pixel data.
///
/// # Panics
///
- /// Panics if the `data` vector is not of length `width * height`.
- pub fn from_parts(data: Vec<PremulRgba8>, width: u16, height: u16) -> Self {
- Self::from_parts_with_opacity(data, width, height, true)
- }
-
- /// Create a new pixmap with the given premultiplied RGBA8 data and precomputed opacity flag.
- ///
- /// The `data` vector must be of length `width * height` exactly.
- ///
- /// The pixels are in row-major order.
- ///
- /// Use this when you've already determined whether the data contains
- /// non-opaque pixels to avoid redundant scanning.
- ///
- /// # Panics
- ///
- /// Panics if the `data` vector is not of length `width * height`.
- pub fn from_parts_with_opacity(
- data: Vec<PremulRgba8>,
+ /// - Panics if `data` is not exactly `width * height * 4` bytes long.
+ /// - Panics if the capacity of the vector is not a multiple of 4.
+ pub fn from_parts(
+ mut data: Vec<u8>,
width: u16,
height: u16,
- may_have_transparency: bool,
+ pixel_metadata: PixelMetadata,
) -> Self {
+ let may_have_transparency = if pixel_metadata.may_have_transparency
+ && pixel_metadata.alpha_type == ImageAlphaType::Alpha
+ {
+ // If there might be transparency and the data is not premultiplied yet, we need to
+ // iterate over all pixels anyway. Rechecking the alpha values only adds little
+ // overhead (around 5-10% from my benchmarks), and lets us downgrade a conservative
+ // transparency hint to fully opaque.
+ premultiply_rgba8(&mut data)
+ } else {
+ // If the data is already premultiplied, we want to avoid reloading all pixels from
+ // memory just to _maybe_ downgrade the transparency hint, so we avoid doing that
+ // and always return the hint directly.
+ pixel_metadata.may_have_transparency
+ };
+
+ let data: Vec<PremulRgba8> = bytemuck::try_cast_vec(data)
+ .map_err(|(error, _data)| error)
+ .expect("The capacity of the vector needs to be divisible by 4.");
assert_eq!(
data.len(),
usize::from(width) * usize::from(height),
"Expected `data` to have length of exactly `width * height`"
);
+
Self {
width,
height,
@@ -298,23 +299,7 @@
}
};
- let mut may_have_transparency = false;
- for pixel in pixmap.data_mut() {
- let alpha = pixel.a;
- if alpha != 255 {
- may_have_transparency = true;
- }
- let alpha_u16 = u16::from(alpha);
- #[expect(
- clippy::cast_possible_truncation,
- reason = "Overflow should be impossible."
- )]
- let premultiply = |e: u8| ((u16::from(e) * alpha_u16) / 255) as u8;
- pixel.r = premultiply(pixel.r);
- pixel.g = premultiply(pixel.g);
- pixel.b = premultiply(pixel.b);
- }
- pixmap.may_have_transparency = may_have_transparency;
+ pixmap.may_have_transparency = premultiply_rgba8(pixmap.data_as_u8_slice_mut());
Ok(pixmap)
}
@@ -434,3 +419,170 @@
.collect()
}
}
+
+/// Metadata about the pixels of an image.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct PixelMetadata {
+ /// Whether the pixels may be non-opaque.
+ ///
+ /// If unsure, always set this to `true`. Setting this to `false` is a strong guarantee that
+ /// every pixel in the image **is guaranteed** to be opaque. Setting this to `false` mistakenly
+ /// can lead to wrong rendering.
+ pub may_have_transparency: bool,
+ /// How the alpha channel is represented.
+ pub alpha_type: ImageAlphaType,
+}
+
+impl PixelMetadata {
+ /// Create a new pixel metadata description.
+ pub const fn new(alpha_type: ImageAlphaType, may_have_transparency: bool) -> Self {
+ Self {
+ may_have_transparency,
+ alpha_type,
+ }
+ }
+}
+
+impl Default for PixelMetadata {
+ fn default() -> Self {
+ Self::new(ImageAlphaType::AlphaPremultiplied, true)
+ }
+}
+
+/// Premultiplies each RGBA8 pixel in `data`.
+///
+/// Returns `true` if at least one pixel is not fully opaque.
+fn premultiply_rgba8(data: &mut [u8]) -> bool {
+ // Unfortunately we need to construct a custom level here and cannot use the one
+ // from the Vello CPU / Vello Hybrid context. This does mean we are not testing
+ // all possible combinations in CI, but the used intrinsics are very simple and
+ // also used in other parts of the pipeline, so risk is very low.
+ let level = Level::try_detect().unwrap_or(Level::baseline());
+
+ dispatch!(level, simd => premultiply_rgba8_impl(simd, data))
+}
+
+#[inline(always)]
+fn premultiply_rgba8_impl<S: Simd>(simd: S, data: &mut [u8]) -> bool {
+ let (body, tail) = data.as_chunks_mut::<64>();
+ let mut transparency = mask8x16::splat(simd, 0);
+
+ for chunk in body {
+ let rgba = simd.load_interleaved_128_u8x64(chunk);
+ let (rg, ba) = simd.split_u8x64(rgba);
+ let (r, g) = simd.split_u8x32(rg);
+ let (b, a) = simd.split_u8x32(ba);
+
+ transparency |= !a.simd_eq(255);
+ let premultiply = {
+ #[inline(always)]
+ |component| {
+ let product = simd.widen_u8x16(component) * simd.widen_u8x16(a);
+ simd.narrow_u16x16(product.div_255())
+ }
+ };
+ let premultiplied = simd.combine_u8x32(
+ simd.combine_u8x16(premultiply(r), premultiply(g)),
+ simd.combine_u8x16(premultiply(b), a),
+ );
+ simd.store_interleaved_128_u8x64(premultiplied, chunk);
+ }
+
+ let mut may_have_transparency = transparency.any_true();
+ for pixel in tail.chunks_exact_mut(4) {
+ let alpha = u16::from(pixel[3]);
+ may_have_transparency |= alpha != 255;
+ let premultiply = |component| ((u16::from(component) * alpha + 255) >> 8) as u8;
+ pixel[0] = premultiply(pixel[0]);
+ pixel[1] = premultiply(pixel[1]);
+ pixel[2] = premultiply(pixel[2]);
+ }
+
+ may_have_transparency
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::vec;
+
+ use super::{PixelMetadata, Pixmap};
+ use crate::peniko::ImageAlphaType;
+
+ #[test]
+ fn straight_alpha_is_premultiplied_in_body_and_tail() {
+ let pixmap = Pixmap::from_parts(
+ vec![
+ // SIMD body
+ 200, 100, 50, 128, 128, 64, 32, 128, 255, 128, 64, 64, 255, 100, 1, 0, 64, 32, 16,
+ 192, 10, 20, 30, 255, 240, 120, 60, 128, 80, 40, 20, 64, 100, 50, 25, 128, 32, 16,
+ 8, 192, 200, 150, 100, 64, 3, 2, 1, 128, 254, 253, 252, 128, 1, 2, 3, 64, 127, 63,
+ 31, 192, 9, 8, 7, 255, // Scalar tail
+ 80, 40, 20, 64,
+ ],
+ 17,
+ 1,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
+ );
+
+ assert!(pixmap.may_have_transparency());
+ assert_eq!(
+ pixmap.data_as_u8_slice(),
+ [
+ // SIMD body
+ 100, 50, 25, 128, 64, 32, 16, 128, 64, 32, 16, 64, 0, 0, 0, 0, 48, 24, 12, 192, 10,
+ 20, 30, 255, 120, 60, 30, 128, 20, 10, 5, 64, 50, 25, 13, 128, 24, 12, 6, 192, 50,
+ 38, 25, 64, 2, 1, 1, 128, 127, 127, 126, 128, 1, 1, 1, 64, 96, 48, 24, 192, 9, 8,
+ 7, 255, // Scalar tail
+ 20, 10, 5, 64,
+ ]
+ );
+ }
+
+ #[test]
+ fn straight_alpha_is_premultiplied_with_only_tail() {
+ let pixmap = Pixmap::from_parts(
+ vec![200, 100, 50, 128, 9, 8, 7, 255],
+ 2,
+ 1,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
+ );
+
+ assert!(pixmap.may_have_transparency());
+ assert_eq!(pixmap.data_as_u8_slice(), [100, 50, 25, 128, 9, 8, 7, 255]);
+ }
+
+ #[test]
+ fn straight_opaque_alpha_clears_transparency_hint_in_body_and_tail() {
+ let data = vec![
+ // SIMD body
+ 200, 100, 50, 255, 1, 2, 3, 255, 4, 5, 6, 255, 7, 8, 9, 255, 10, 11, 12, 255, 13, 14,
+ 15, 255, 16, 17, 18, 255, 19, 20, 21, 255, 22, 23, 24, 255, 25, 26, 27, 255, 28, 29,
+ 30, 255, 31, 32, 33, 255, 34, 35, 36, 255, 37, 38, 39, 255, 40, 41, 42, 255, 43, 44,
+ 45, 255, // Scalar tail
+ 80, 40, 20, 255,
+ ];
+ let pixmap = Pixmap::from_parts(
+ data.clone(),
+ 17,
+ 1,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
+ );
+
+ assert!(!pixmap.may_have_transparency());
+ assert_eq!(pixmap.data_as_u8_slice(), data);
+ }
+
+ #[test]
+ fn straight_opaque_alpha_clears_transparency_hint_with_only_tail() {
+ let data = vec![1, 2, 3, 255];
+ let pixmap = Pixmap::from_parts(
+ data.clone(),
+ 1,
+ 1,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
+ );
+
+ assert!(!pixmap.may_have_transparency());
+ assert_eq!(pixmap.data_as_u8_slice(), data);
+ }
+}
diff --git a/sparse_strips/vello_cpu/src/render.rs b/sparse_strips/vello_cpu/src/render.rs
index f2d89e5..8a2edef 100644
--- a/sparse_strips/vello_cpu/src/render.rs
+++ b/sparse_strips/vello_cpu/src/render.rs
@@ -971,7 +971,8 @@
use vello_common::color::PremulRgba8;
use vello_common::color::palette::css::{BLUE, RED};
use vello_common::kurbo::{Rect, Shape};
- use vello_common::pixmap::{Pixmap, PixmapMut};
+ use vello_common::peniko::ImageAlphaType;
+ use vello_common::pixmap::{PixelMetadata, Pixmap, PixmapMut};
use vello_common::tile::Tile;
const GRAY: PremulRgba8 = PremulRgba8 {
@@ -995,9 +996,10 @@
fn solid_pixmap(width: u16, height: u16, color: PremulRgba8) -> Pixmap {
Pixmap::from_parts(
- vec![color; usize::from(width) * usize::from(height)],
+ bytemuck::cast_vec(vec![color; usize::from(width) * usize::from(height)]),
width,
height,
+ PixelMetadata::new(ImageAlphaType::AlphaPremultiplied, color.a != 255),
)
}
diff --git a/sparse_strips/vello_example_scenes/src/image.rs b/sparse_strips/vello_example_scenes/src/image.rs
index 75efa64..d53074b 100644
--- a/sparse_strips/vello_example_scenes/src/image.rs
+++ b/sparse_strips/vello_example_scenes/src/image.rs
@@ -5,11 +5,9 @@
use std::f64::consts::PI;
use std::io::Cursor;
-use vello_common::color::PremulRgba8;
use vello_common::kurbo::{BezPath, Point, Shape, Vec2};
-use vello_common::peniko::ImageFormat;
-use vello_common::peniko::ImageSampler;
-use vello_common::pixmap::Pixmap;
+use vello_common::peniko::{ImageAlphaType, ImageFormat, ImageSampler};
+use vello_common::pixmap::{PixelMetadata, Pixmap};
use vello_common::{
kurbo::{Affine, Rect},
paint::{Image, ImageSource},
@@ -186,24 +184,11 @@
#[expect(clippy::cast_possible_truncation, reason = "deliberate quantization")]
pub fn read_image(data: &[u8]) -> Pixmap {
let image_data = decode_image(data);
- let premul_data: Vec<PremulRgba8> = image_data
- .data
- .chunks_exact(4)
- .map(|rgba| {
- let alpha = u16::from(rgba[3]);
- let premultiply = |component| (alpha * (u16::from(component)) / 255) as u8;
- PremulRgba8 {
- r: premultiply(rgba[0]),
- g: premultiply(rgba[1]),
- b: premultiply(rgba[2]),
- a: alpha as u8,
- }
- })
- .collect();
Pixmap::from_parts(
- premul_data,
+ image_data.data,
image_data.width as u16,
image_data.height as u16,
+ PixelMetadata::new(ImageAlphaType::Alpha, true),
)
}
diff --git a/sparse_strips/vello_hybrid/examples/render_to_file.rs b/sparse_strips/vello_hybrid/examples/render_to_file.rs
index 271c585..a075f28 100644
--- a/sparse_strips/vello_hybrid/examples/render_to_file.rs
+++ b/sparse_strips/vello_hybrid/examples/render_to_file.rs
@@ -9,7 +9,7 @@
use std::io::BufWriter;
use vello_common::kurbo::{Affine, Stroke};
use vello_common::pico_svg::{Item, PicoSvg};
-use vello_common::pixmap::Pixmap;
+use vello_common::pixmap::{PixelMetadata, Pixmap};
use vello_hybrid::{DimensionConstraints, Scene};
/// Main entry point for the headless rendering example.
@@ -149,18 +149,18 @@
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
// Read back the pixel data
- let mut img_data = Vec::with_capacity(usize::from(width) * usize::from(height));
+ let mut img_data = Vec::with_capacity(usize::from(width) * usize::from(height) * 4);
for row in texture_copy_buffer
.slice(..)
.get_mapped_range()
.chunks_exact(bytes_per_row as usize)
{
- img_data.extend_from_slice(bytemuck::cast_slice(&row[0..usize::from(width) * 4]));
+ img_data.extend_from_slice(&row[0..usize::from(width) * 4]);
}
texture_copy_buffer.unmap();
// Create the pixmap from the image data
- let pixmap = Pixmap::from_parts(img_data, width, height);
+ let pixmap = Pixmap::from_parts(img_data, width, height, PixelMetadata::default());
// Write the pixmap to a file
let file = std::fs::File::create(output_filename).unwrap();
diff --git a/sparse_strips/vello_sparse_tests/snapshots/external_texture_clipped.png b/sparse_strips/vello_sparse_tests/snapshots/external_texture_clipped.png
index abbbe7e..55c2509 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/external_texture_clipped.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/external_texture_clipped.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c281c946e976c28d79537872006f42c73e425a3116bc14775502b7d6af7dde44
-size 2429
+oid sha256:d606d491f1f16774a92f4b6ae00415c2cf2c989e00ffe59c2fdb3a27b6581e81
+size 2440
diff --git a/sparse_strips/vello_sparse_tests/snapshots/external_texture_composite.png b/sparse_strips/vello_sparse_tests/snapshots/external_texture_composite.png
index df445fd..3717b50 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/external_texture_composite.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/external_texture_composite.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:65f0c8e77595a6725ac6d64613cb809308df84defee4d7a168ef08d5bc3a68ac
-size 4062
+oid sha256:814a9064e79345b6130a889fd73e3a0f01dec56cd20a15600cbd53af5baa3a18
+size 4067
diff --git a/sparse_strips/vello_sparse_tests/snapshots/external_texture_many_sprites.png b/sparse_strips/vello_sparse_tests/snapshots/external_texture_many_sprites.png
index a25b90b..c7fa077 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/external_texture_many_sprites.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/external_texture_many_sprites.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:23b105e564a4c74da3e5757b478841709e1e2f1d6496f8534f745bbd00299885
-size 13239
+oid sha256:08eae45eae7b2b9e41e112ee29a274444c3eeab049fd6dfbca10951b8f81c1a7
+size 13253
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple.png
index 8b530a8..d166336 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:63f1b46d7f1fb05972aef00ac06798751e3ffba0d6112facc333e81977a3fcbd
-size 14019
+oid sha256:550ed276d15fccbf29b13b85999683b20181b88db5e971b0ed34e38480c560e7
+size 13998
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple_cached.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple_cached.png
index f3fbb7e..7ea1566 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple_cached.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_apple_cached.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c54ae9c4dfccc0cf051f06936efe151e8e6703b0cadf9f7688d75d407d485ea5
-size 14002
+oid sha256:a09d496746cb6ea78967b40df1a44918a8b5e4d111e743b824b130fcbc09bfa6
+size 13991
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto.png
index 5751b9d..b2589f7 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:42c171625da5fcbbb9037fc45ec81c884abbda617bea8b8f41496f60456e3a78
-size 10033
+oid sha256:3130281802bb6e01d4bb8c0295d28a58f60fdda305e97b98d346a2a314230e96
+size 10003
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_cached.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_cached.png
index 3898d68..ae739ea 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_cached.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_cached.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:55afd683335ead381dca8b20525ce8870eb7aa50a7449cb3e4227e9990a7b848
-size 10022
+oid sha256:91072a315b734618118d99e586d2f6904eb1da56c237417e323107f6382b1da9
+size 9948
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_stroked.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_stroked.png
index 5751b9d..b2589f7 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_stroked.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_bitmap_noto_stroked.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:42c171625da5fcbbb9037fc45ec81c884abbda617bea8b8f41496f60456e3a78
-size 10033
+oid sha256:3130281802bb6e01d4bb8c0295d28a58f60fdda305e97b98d346a2a314230e96
+size 10003
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap.png
index 225ba6a..c3bcb69 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9ec6f88f508422774b5b5d83dc7a66f66ff1b38939e6f91e7d59877459f24034
-size 21228
+oid sha256:ade70b47ea0b61da1d08bb01c7e122343d6b8228e63e9173cd3483b3ae844341
+size 21215
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_cached.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_cached.png
index b1330ea..8a61b53 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_cached.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_cached.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e6dc626db65586336abd37b3de3fffc763af5239c5344b2123ac90c1796ed9ea
-size 21179
+oid sha256:1e506f44906d4a2fc712d4480b45b29d77e7b2bdf4c9d327c2a9c88612890a86
+size 21175
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted.png
index 225ba6a..c3bcb69 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9ec6f88f508422774b5b5d83dc7a66f66ff1b38939e6f91e7d59877459f24034
-size 21228
+oid sha256:ade70b47ea0b61da1d08bb01c7e122343d6b8228e63e9173cd3483b3ae844341
+size 21215
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted_cached.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted_cached.png
index b1330ea..8a61b53 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted_cached.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_transform_composition_rows_bitmap_hinted_cached.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e6dc626db65586336abd37b3de3fffc763af5239c5344b2123ac90c1796ed9ea
-size 21179
+oid sha256:1e506f44906d4a2fc712d4480b45b29d77e7b2bdf4c9d327c2a9c88612890a86
+size 21175
diff --git a/sparse_strips/vello_sparse_tests/snapshots/image_lumaa_image.png b/sparse_strips/vello_sparse_tests/snapshots/image_lumaa_image.png
index 3c65194..4a6f9ad 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/image_lumaa_image.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/image_lumaa_image.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e0bdb672b8d45eb81d023039f34fb54c3e594c094e9b8463b9d2618ba9d9bb18
+oid sha256:70e0565e7439b2cf75e2e987b7a37fc9795f5be570f5954040b62609cf2a2ec3
size 154
diff --git a/sparse_strips/vello_sparse_tests/snapshots/image_rgba_image.png b/sparse_strips/vello_sparse_tests/snapshots/image_rgba_image.png
index ab6244b..15c48f2 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/image_rgba_image.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/image_rgba_image.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1b95b70ff1685b84e98b8b43eca5750b71497757fcc8f007a97d2a979ac5d23e
+oid sha256:7834fdcf54255a46383d2e62899217e37dc728a7583f07a31d3a7ed7f46f0cfa
size 154
diff --git a/sparse_strips/vello_sparse_tests/snapshots/issue_bicubic_filtering_clamping.png b/sparse_strips/vello_sparse_tests/snapshots/issue_bicubic_filtering_clamping.png
index 3f0e961..2061c23 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/issue_bicubic_filtering_clamping.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/issue_bicubic_filtering_clamping.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e5abec3e076f454eef4f245ff4a4e822416791dbf6135e392c702c91337bb3ae
-size 232
+oid sha256:9243a1dda3048068cd4fc9ac811a7ec7e72408d80528efde8fd07f123cf89e7d
+size 229
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_color.png b/sparse_strips/vello_sparse_tests/snapshots/mix_color.png
index 65ddcbc..e32818b 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_color.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_color.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:de8d4f9ed15ae0f074021d21670eba70c9e8390ae1b5a4b44bc6c71521982685
-size 4624
+oid sha256:4143d6ab1b110e5bbb09731c60d7558b63439554aa2ac2820ae16abe242ff657
+size 4608
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_color_burn.png b/sparse_strips/vello_sparse_tests/snapshots/mix_color_burn.png
index b33d226..18cc567 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_color_burn.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_color_burn.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9d6962fd98ea18eef745df863babcc63b4164d126f667b2e00c9349a189999c9
-size 4033
+oid sha256:13b6c11ff0d0253220630b4659caf6f142b7dd7719c3368487bd408ebaf3e607
+size 4012
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_color_dodge.png b/sparse_strips/vello_sparse_tests/snapshots/mix_color_dodge.png
index f17e5d4..1f752c7 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_color_dodge.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_color_dodge.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a4307d77bbb1e8416fa4af158c6d451ddab6179f7f75e5d3e85fc7adb8fa1ce0
-size 3986
+oid sha256:d34ac3c245abcaaf23ecec2792b6149ae47703e35aa8ffa9b72f28a9609b12d7
+size 3999
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_darken.png b/sparse_strips/vello_sparse_tests/snapshots/mix_darken.png
index d086c44..8c12866 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_darken.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_darken.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:17b943cc82ec9eebd93a735498308deeb791d7dcf054d77576ca6cb81c696d5c
-size 4021
+oid sha256:3ce5f9ef84183fa782fde14675580d53e3223d4ea2049ade94edbf7ff112855c
+size 4022
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_difference.png b/sparse_strips/vello_sparse_tests/snapshots/mix_difference.png
index 8fe1479..7ac244c 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_difference.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_difference.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b0defe1ea8e8ddc9038d1992f5d207d0441c6ec7b4589650902ca90f78493d61
-size 4584
+oid sha256:ff619c0d5f82935bb5bcfd1f21b7f23d97ab4e7a4b3156afe83387dd49991028
+size 4574
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_exclusion.png b/sparse_strips/vello_sparse_tests/snapshots/mix_exclusion.png
index 40c01c7..d0f6db8 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_exclusion.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_exclusion.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f2ce363b588972c9debab113b929c2f9287939ae4dceba8bf11eb97d670353a6
+oid sha256:319c95193bf0acf0364c831d9557b5def1fea85c6444747180b4f171876eb6cc
size 4436
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_hard_light.png b/sparse_strips/vello_sparse_tests/snapshots/mix_hard_light.png
index b64df82..a5620fb 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_hard_light.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_hard_light.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c3aa1fe2316ec2ad1df4ce7863853420b49d5fb0f29011461b09829ea6855992
-size 3674
+oid sha256:eee15d08e065fdecf0212e6a82b2eea64f49963eea4abfda6855931aa0e6c5eb
+size 3666
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_hue.png b/sparse_strips/vello_sparse_tests/snapshots/mix_hue.png
index 2f3bcf5..a16663e 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_hue.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_hue.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:55739c0cbab808b79d2e42432d4dd55ed8a8a75a5136d17561b7e6525b569a13
-size 4705
+oid sha256:ba383d849ce8ab28108f19e964c97555b8aadef0204c623ae23b4548d032d285
+size 4703
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_lighten.png b/sparse_strips/vello_sparse_tests/snapshots/mix_lighten.png
index e2b0c1d..235f507 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_lighten.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_lighten.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:77f33456014a0ff432da86d26c899990e36db3867da1d4b9ba8555cf003b3765
-size 3962
+oid sha256:e5e372a412bc379c8d8711214fe8a87a941b38cd8ba7184eb0a57a168a5afb6a
+size 3967
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_luminosity.png b/sparse_strips/vello_sparse_tests/snapshots/mix_luminosity.png
index cc06d10..d79cf2c 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_luminosity.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_luminosity.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:822e16380c4d79396ae72ba67045658be52ab8f8796033d32e86de498516f800
-size 4076
+oid sha256:7006aa4051c73ea897fda880026f78cd456874b79af27baf57badbeb9e6ca952
+size 4063
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_multiply.png b/sparse_strips/vello_sparse_tests/snapshots/mix_multiply.png
index fe23d18..624a73b 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_multiply.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_multiply.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e19c5e256131360602d5b2dc7c8b1aacd572aa315a9ae77e792ad42602239f80
-size 4188
+oid sha256:247ea14930d62bf3e809bc47d3f4776ccad289f5212199f94a109178ba5c5fe4
+size 4186
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_normal.png b/sparse_strips/vello_sparse_tests/snapshots/mix_normal.png
index 64dc7af..470ce3d 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_normal.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_normal.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7b78fab50fdc988b329b02556aa92bb1dd95d246589c012be9de47bf931c7b55
-size 3112
+oid sha256:6cdc729e4772e7ad58fe2fcb70a4e07847de3046c69b7f5a58291389849f851a
+size 3106
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_overlay.png b/sparse_strips/vello_sparse_tests/snapshots/mix_overlay.png
index fa63569..e291ac2 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_overlay.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_overlay.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b881c92de93cfbc5f337e8647b166d60cf32dedb3e6b4b00db8126b73ec85cc3
-size 4222
+oid sha256:7a3363437dbcf1527587501af145a4155d0082e60d421c65aa3b8deca734fb62
+size 4238
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_saturation.png b/sparse_strips/vello_sparse_tests/snapshots/mix_saturation.png
index 539c568..42f5e2a 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_saturation.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_saturation.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5034012b76d5a7f6e30189af67258bcde179a45dce77f11f817d39058a794309
-size 3483
+oid sha256:7878848a7b0f5195dae66555b1ff39684769d2d62a66d04e89563e16bcf46d24
+size 3480
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_screen.png b/sparse_strips/vello_sparse_tests/snapshots/mix_screen.png
index 5f5deb9..d97ee5b 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_screen.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_screen.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a8c4b2f4020c4a17b0a582e1ec9783211a952cf2ac36f6dde235abfac4167e41
-size 4038
+oid sha256:83651872587f1d7ffea39b09059c9711ba97d1a95ffd5b7eee7cfc17252a16e2
+size 4041
diff --git a/sparse_strips/vello_sparse_tests/snapshots/mix_soft_light.png b/sparse_strips/vello_sparse_tests/snapshots/mix_soft_light.png
index ba03028..c9b5297 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/mix_soft_light.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/mix_soft_light.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:49afb9596a273043eb669ac4b015ad705fba78eb0e5b9ac7ae35c1a2ba0ccdf0
-size 4525
+oid sha256:bd2d0d1d0d480b7dd72baecff11cf3229aaaed3ea39267c1fb2495a8e9fd3988
+size 4534
diff --git a/sparse_strips/vello_sparse_tests/tests/external_texture.rs b/sparse_strips/vello_sparse_tests/tests/external_texture.rs
index 885093c..8dae50b 100644
--- a/sparse_strips/vello_sparse_tests/tests/external_texture.rs
+++ b/sparse_strips/vello_sparse_tests/tests/external_texture.rs
@@ -5,13 +5,13 @@
use std::sync::Arc;
use vello_common::color;
- use vello_common::color::{AlphaColor, PremulRgba8, Srgb};
+ use vello_common::color::{AlphaColor, Srgb};
use vello_common::filter_effects::{EdgeMode, Filter, FilterPrimitive};
use vello_common::geometry::RectU16;
use vello_common::kurbo::{Affine, Circle, Rect, Shape};
use vello_common::paint::{Image, ImageSource, Tint, TintMode};
- use vello_common::peniko::{Color, Extend, ImageQuality, ImageSampler};
- use vello_common::pixmap::Pixmap;
+ use vello_common::peniko::{Color, Extend, ImageAlphaType, ImageQuality, ImageSampler};
+ use vello_common::pixmap::{PixelMetadata, Pixmap};
use vello_dev_macros::vello_test;
use vello_hybrid::TextureId;
@@ -31,9 +31,10 @@
fn solid_pixmap(r: u8, g: u8, b: u8, a: u8) -> Arc<Pixmap> {
Arc::new(Pixmap::from_parts(
- vec![PremulRgba8::from_u8_array([r, g, b, a])],
+ vec![r, g, b, a],
1,
1,
+ PixelMetadata::new(ImageAlphaType::AlphaPremultiplied, a != 255),
))
}
@@ -383,7 +384,7 @@
);
}
- #[vello_test(width = 96, height = 96, hybrid_only)]
+ #[vello_test(width = 96, height = 96, hybrid_only, hybrid_tolerance = 1)]
fn external_texture_skewed(ctx: &mut impl Renderer) {
let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto"));
let source_region = SPRITES[0];
diff --git a/sparse_strips/vello_sparse_tests/tests/glyph.rs b/sparse_strips/vello_sparse_tests/tests/glyph.rs
index d3a7b86..08b815a 100644
--- a/sparse_strips/vello_sparse_tests/tests/glyph.rs
+++ b/sparse_strips/vello_sparse_tests/tests/glyph.rs
@@ -21,7 +21,7 @@
use vello_common::peniko::{
Blob, Extend, FontData, Gradient, ImageQuality, ImageSampler, LinearGradientPosition,
};
-use vello_common::pixmap::Pixmap;
+use vello_common::pixmap::{PixelMetadata, Pixmap};
use vello_dev_macros::vello_test;
fn render_transform_composition_rows(
@@ -462,12 +462,18 @@
let pixels = color_strip
.0
.iter()
- .map(|stop| {
+ .flat_map(|stop| {
PremulColor::from_alpha_color(stop.color.to_alpha_color::<Srgb>())
.as_premul_rgba8()
+ .to_u8_array()
})
.collect();
- let image = ctx.get_image_source(Arc::new(Pixmap::from_parts(pixels, 4, 1)));
+ let image = ctx.get_image_source(Arc::new(Pixmap::from_parts(
+ pixels,
+ 4,
+ 1,
+ PixelMetadata::default(),
+ )));
PaintType::from(Image {
image,
sampler: ImageSampler {
diff --git a/sparse_strips/vello_sparse_tests/tests/issues.rs b/sparse_strips/vello_sparse_tests/tests/issues.rs
index 7ebb9fb..96b8a45 100644
--- a/sparse_strips/vello_sparse_tests/tests/issues.rs
+++ b/sparse_strips/vello_sparse_tests/tests/issues.rs
@@ -20,7 +20,7 @@
InterpolationAlphaSpace, Mix,
};
use vello_common::peniko::{ColorStops, RadialGradientPosition};
-use vello_common::pixmap::Pixmap;
+use vello_common::pixmap::{PixelMetadata, Pixmap};
use vello_cpu::color::palette::css::{BLACK, RED};
use vello_cpu::peniko::{Compose, Extend};
use vello_cpu::{Level, RasterizerSettings, RenderContext, RenderMode, RenderSettings};
@@ -571,7 +571,7 @@
b, b, b, b
];
- let pixmap = Pixmap::from_parts(image, 4, 4);
+ let pixmap = Pixmap::from_parts(bytemuck::cast_vec(image), 4, 4, PixelMetadata::default());
let source = ctx.get_image_source(Arc::new(pixmap));
let image = Image {
image: source,