add tests
diff --git a/sparse_strips/vello_dev_macros/src/test.rs b/sparse_strips/vello_dev_macros/src/test.rs
index 497b708..44d08b9 100644
--- a/sparse_strips/vello_dev_macros/src/test.rs
+++ b/sparse_strips/vello_dev_macros/src/test.rs
@@ -225,7 +225,7 @@
let mut ctx = get_ctx::<Scene>(#width, #height, #transparent);
#input_fn_name(&mut ctx);
if !#no_ref {
- check_ref(&ctx, #input_fn_name_str, #webgl_fn_name_str, #hybrid_tolerance, false, RenderMode::OptimizeSpeed, #reference_image_name);
+ check_ref(&ctx, "", #webgl_fn_name_str, #hybrid_tolerance, false, RenderMode::OptimizeSpeed, #reference_image_name);
}
}
};
diff --git a/sparse_strips/vello_sparse_tests/snapshots/scene_larger_than_surface.png b/sparse_strips/vello_sparse_tests/snapshots/scene_larger_than_surface.png
new file mode 100644
index 0000000..183612e
--- /dev/null
+++ b/sparse_strips/vello_sparse_tests/snapshots/scene_larger_than_surface.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c84c102a586cdde6dd01c0ab2ed5ac98e75d6fc69b7b2ffad6860944818c4c0
+size 93
diff --git a/sparse_strips/vello_sparse_tests/snapshots/scene_smaller_than_surface.png b/sparse_strips/vello_sparse_tests/snapshots/scene_smaller_than_surface.png
new file mode 100644
index 0000000..c210428
--- /dev/null
+++ b/sparse_strips/vello_sparse_tests/snapshots/scene_smaller_than_surface.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:57ce9fcc9ac98052af7274e3dfd3ac4a93a33594cd962803d80e886ec3a94804
+size 127
diff --git a/sparse_strips/vello_sparse_tests/tests/mod.rs b/sparse_strips/vello_sparse_tests/tests/mod.rs
index 4d9f627..1bd174a 100644
--- a/sparse_strips/vello_sparse_tests/tests/mod.rs
+++ b/sparse_strips/vello_sparse_tests/tests/mod.rs
@@ -36,5 +36,6 @@
mod mask;
mod mix;
mod opacity;
+mod render_size;
mod renderer;
mod util;
diff --git a/sparse_strips/vello_sparse_tests/tests/render_size.rs b/sparse_strips/vello_sparse_tests/tests/render_size.rs
new file mode 100644
index 0000000..cf5b4e4
--- /dev/null
+++ b/sparse_strips/vello_sparse_tests/tests/render_size.rs
@@ -0,0 +1,209 @@
+// Copyright 2025 the Vello Authors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! Tests where the surface dimensions and scene dimensions do not match.
+//!
+//! Note: These tests do not use vello_test proc macro as they test an edge case where the render
+//! surface is a different size than the scene dimensions.
+
+use crate::renderer::Renderer;
+use crate::util::{check_ref_encoded, pixmap_to_png};
+use vello_common::color::palette::css::{LIME, REBECCA_PURPLE};
+use vello_common::kurbo::Rect;
+use vello_common::pixmap::Pixmap;
+use vello_hybrid::Scene;
+
+fn draw_simple_scene(scene: &mut Scene, width: f64, height: f64) {
+ scene.set_paint(LIME.into());
+ // Cover background of scene
+ scene.fill_rect(&Rect::new(0.0, 0.0, width, height));
+
+ scene.set_paint(REBECCA_PURPLE.into());
+ scene.fill_rect(&Rect::new(10., 20., width - 10., height - 20.));
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+#[test]
+fn scene_smaller_than_surface_hybrid() {
+ let scene_width = 50;
+ let scene_height = 50;
+ let surface_width = 80;
+ let surface_height = 80;
+
+ let mut scene = Scene::new(scene_width, scene_height);
+ draw_simple_scene(&mut scene, scene_width as f64, scene_height as f64);
+
+ let mut pixmap = Pixmap::new(surface_width, surface_height);
+
+ scene.render_to_pixmap(&mut pixmap, vello_cpu::RenderMode::OptimizeSpeed);
+ let encoded_image = pixmap_to_png(pixmap, surface_width as u32, surface_height as u32);
+
+ check_ref_encoded(
+ &encoded_image,
+ "scene_smaller_than_surface",
+ "scene_smaller_than_surface_hybrid",
+ 1,
+ false,
+ &[],
+ );
+}
+
+#[cfg(all(target_arch = "wasm32", feature = "webgl"))]
+#[wasm_bindgen_test::wasm_bindgen_test]
+async fn scene_smaller_than_surface_hybrid_webgl() {
+ use wasm_bindgen::JsCast;
+ use web_sys::{HtmlCanvasElement, WebGl2RenderingContext};
+
+ let scene_width = 50;
+ let scene_height = 50;
+ let surface_width = 80;
+ let surface_height = 80;
+
+ let mut scene = Scene::new(scene_width, scene_height);
+ draw_simple_scene(&mut scene, scene_width as f64, scene_height as f64);
+
+ let document = web_sys::window().unwrap().document().unwrap();
+ let canvas = document
+ .create_element("canvas")
+ .unwrap()
+ .dyn_into::<HtmlCanvasElement>()
+ .unwrap();
+
+ canvas.set_width(surface_width as u32);
+ canvas.set_height(surface_height as u32);
+
+ // Render the smaller scene to the larger canvas
+ let mut renderer = vello_hybrid::WebGlRenderer::new(&canvas);
+ let render_size = vello_hybrid::RenderSize {
+ width: scene_width as u32,
+ height: scene_height as u32,
+ };
+
+ renderer.render(&scene, &render_size).unwrap();
+
+ let gl = canvas
+ .get_context("webgl2")
+ .unwrap()
+ .unwrap()
+ .dyn_into::<WebGl2RenderingContext>()
+ .unwrap();
+
+ let mut pixels = vec![0_u8; (surface_width as usize) * (surface_height as usize) * 4];
+ gl.read_pixels_with_opt_u8_array(
+ 0,
+ 0,
+ surface_width.into(),
+ surface_height.into(),
+ WebGl2RenderingContext::RGBA,
+ WebGl2RenderingContext::UNSIGNED_BYTE,
+ Some(&mut pixels),
+ )
+ .unwrap();
+
+ let mut pixmap = Pixmap::new(surface_width, surface_height);
+ pixmap.data_as_u8_slice_mut().copy_from_slice(&pixels);
+
+ let encoded_image = pixmap_to_png(pixmap, surface_width as u32, surface_height as u32);
+
+ check_ref_encoded(
+ &encoded_image,
+ "",
+ "scene_smaller_than_surface_hybrid_webgl",
+ 1,
+ false,
+ include_bytes!("../snapshots/scene_smaller_than_surface.png"),
+ );
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+#[test]
+fn scene_larger_than_surface_hybrid() {
+ let scene_width = 80;
+ let scene_height = 80;
+ let surface_width = 50;
+ let surface_height = 50;
+
+ let mut scene = Scene::new(scene_width, scene_height);
+ draw_simple_scene(&mut scene, scene_width as f64, scene_height as f64);
+
+ let mut pixmap = Pixmap::new(surface_width, surface_height);
+
+ scene.render_to_pixmap(&mut pixmap, vello_cpu::RenderMode::OptimizeSpeed);
+ let encoded_image = pixmap_to_png(pixmap, surface_width as u32, surface_height as u32);
+
+ check_ref_encoded(
+ &encoded_image,
+ "scene_larger_than_surface",
+ "scene_larger_than_surface_hybrid",
+ 1,
+ false,
+ &[],
+ );
+}
+
+#[cfg(all(target_arch = "wasm32", feature = "webgl"))]
+#[wasm_bindgen_test::wasm_bindgen_test]
+async fn scene_larger_than_surface_hybrid_webgl() {
+ use wasm_bindgen::JsCast;
+ use web_sys::{HtmlCanvasElement, WebGl2RenderingContext};
+
+ let scene_width = 80;
+ let scene_height = 80;
+ let surface_width = 50;
+ let surface_height = 50;
+
+ let mut scene = Scene::new(scene_width, scene_height);
+ draw_simple_scene(&mut scene, scene_width as f64, scene_height as f64);
+
+ let document = web_sys::window().unwrap().document().unwrap();
+ let canvas = document
+ .create_element("canvas")
+ .unwrap()
+ .dyn_into::<HtmlCanvasElement>()
+ .unwrap();
+
+ canvas.set_width(surface_width as u32);
+ canvas.set_height(surface_height as u32);
+
+ // Render the larger scene to the smaller canvas
+ let mut renderer = vello_hybrid::WebGlRenderer::new(&canvas);
+ let render_size = vello_hybrid::RenderSize {
+ width: scene_width as u32,
+ height: scene_height as u32,
+ };
+
+ renderer.render(&scene, &render_size).unwrap();
+
+ let gl = canvas
+ .get_context("webgl2")
+ .unwrap()
+ .unwrap()
+ .dyn_into::<WebGl2RenderingContext>()
+ .unwrap();
+
+ let mut pixels = vec![0_u8; (surface_width as usize) * (surface_height as usize) * 4];
+ gl.read_pixels_with_opt_u8_array(
+ 0,
+ 0,
+ surface_width.into(),
+ surface_height.into(),
+ WebGl2RenderingContext::RGBA,
+ WebGl2RenderingContext::UNSIGNED_BYTE,
+ Some(&mut pixels),
+ )
+ .unwrap();
+
+ let mut pixmap = Pixmap::new(surface_width, surface_height);
+ pixmap.data_as_u8_slice_mut().copy_from_slice(&pixels);
+
+ let encoded_image = pixmap_to_png(pixmap, surface_width as u32, surface_height as u32);
+
+ check_ref_encoded(
+ &encoded_image,
+ "",
+ "scene_larger_than_surface_hybrid_webgl",
+ 1,
+ false,
+ include_bytes!("../snapshots/scene_larger_than_surface.png"),
+ );
+}
diff --git a/sparse_strips/vello_sparse_tests/tests/renderer.rs b/sparse_strips/vello_sparse_tests/tests/renderer.rs
index 64b0072..c0f6644 100644
--- a/sparse_strips/vello_sparse_tests/tests/renderer.rs
+++ b/sparse_strips/vello_sparse_tests/tests/renderer.rs
@@ -236,8 +236,8 @@
M.lock().unwrap()
};
- let width = self.width();
- let height = self.height();
+ let width = pixmap.width();
+ let height = pixmap.height();
// Copied from vello_hybrid/examples/`render_to_file.rs`.
@@ -369,8 +369,8 @@
use wasm_bindgen::JsCast;
use web_sys::{HtmlCanvasElement, WebGl2RenderingContext};
- let width = self.width();
- let height = self.height();
+ let width = pixmap.width();
+ let height = pixmap.height();
// Create an offscreen HTMLCanvasElement, render the test image to it, and finally read off
// the pixmap for diff checking.
diff --git a/sparse_strips/vello_sparse_tests/tests/util.rs b/sparse_strips/vello_sparse_tests/tests/util.rs
index 26d4573..be7374a 100644
--- a/sparse_strips/vello_sparse_tests/tests/util.rs
+++ b/sparse_strips/vello_sparse_tests/tests/util.rs
@@ -230,7 +230,6 @@
png_data
}
-#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn check_ref(
ctx: &impl Renderer,
// The name of the test.
@@ -242,83 +241,101 @@
threshold: u8,
// Whether the test instance is the "gold standard" and should be used
// for creating reference images.
+ // N.B. Must be `false` on `wasm32` as reference image cannot be written to filesystem.
is_reference: bool,
render_mode: RenderMode,
- _: &[u8],
+ // `ref_data` is the reference image data, passed directly for wasm32.
+ ref_data: &[u8],
) {
let pixmap = render_pixmap(ctx, render_mode);
-
let encoded_image = pixmap_to_png(pixmap, ctx.width() as u32, ctx.height() as u32);
- let ref_path = REFS_PATH.join(format!("{}.png", test_name));
- let write_ref_image = || {
- let optimized =
- oxipng::optimize_from_memory(&encoded_image, &oxipng::Options::max_compression())
- .unwrap();
- std::fs::write(&ref_path, optimized).unwrap();
- };
-
- if !ref_path.exists() {
- if is_reference {
- write_ref_image();
- panic!("new reference image was created");
- } else {
- panic!("no reference image exists");
- }
- }
-
- let ref_image = load_from_memory(&std::fs::read(&ref_path).unwrap())
- .unwrap()
- .into_rgba8();
- let actual = load_from_memory(&encoded_image).unwrap().into_rgba8();
-
- let diff_image = get_diff(&ref_image, &actual, threshold);
-
- if let Some(diff_image) = diff_image {
- if std::env::var("REPLACE").is_ok() && is_reference {
- write_ref_image();
- panic!("test was replaced");
- }
-
- if !DIFFS_PATH.exists() {
- let _ = std::fs::create_dir_all(DIFFS_PATH.as_path());
- }
-
- let diff_path = DIFFS_PATH.join(format!("{}.png", specific_name));
- diff_image
- .save_with_format(&diff_path, image::ImageFormat::Png)
- .unwrap();
-
- panic!("test didnt match reference image");
- }
+ check_ref_encoded(
+ &encoded_image,
+ test_name,
+ specific_name,
+ threshold,
+ is_reference,
+ ref_data,
+ );
}
-#[cfg(target_arch = "wasm32")]
-pub(crate) fn check_ref(
- ctx: &impl Renderer,
- _test_name: &str,
+pub(crate) fn check_ref_encoded(
+ // The encoded image under test.
+ encoded_image: &[u8],
+ // The name of the test.
+ test_name: &str,
// The name of the specific instance of the test that is being run
// (e.g. test_gpu, test_cpu_u8, etc.)
specific_name: &str,
// Tolerance for pixel differences.
threshold: u8,
- // Must be `false` on `wasm32` as reference image cannot be written to filesystem.
+ // Whether the test instance is the "gold standard" and should be used
+ // for creating reference images.
+ // N.B. Must be `false` on `wasm32` as reference image cannot be written to filesystem.
is_reference: bool,
- render_mode: RenderMode,
+ // `ref_data` is the reference image data, passed directly for wasm32.
ref_data: &[u8],
) {
- assert!(!is_reference, "WASM cannot create new reference images");
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ assert_eq!(ref_data.len(), 0, "ref_data is only used for wasm32 tests");
+ let ref_path = REFS_PATH.join(format!("{}.png", test_name));
- let pixmap = render_pixmap(ctx, render_mode);
- let encoded_image = pixmap_to_png(pixmap, ctx.width() as u32, ctx.height() as u32);
- let actual = load_from_memory(&encoded_image).unwrap().into_rgba8();
+ let write_ref_image = || {
+ let optimized =
+ oxipng::optimize_from_memory(&encoded_image, &oxipng::Options::max_compression())
+ .unwrap();
+ std::fs::write(&ref_path, optimized).unwrap();
+ };
- let ref_image = load_from_memory(ref_data).unwrap().into_rgba8();
+ if !ref_path.exists() {
+ if is_reference {
+ write_ref_image();
+ panic!("new reference image was created");
+ } else {
+ panic!("no reference image exists");
+ }
+ }
- let diff_image = get_diff(&ref_image, &actual, threshold);
- if let Some(ref img) = diff_image {
- append_diff_image_to_browser_document(specific_name, img);
- panic!("test didn't match reference image. Scroll to bottom of browser to view diff.");
+ let ref_image = load_from_memory(&std::fs::read(&ref_path).unwrap())
+ .unwrap()
+ .into_rgba8();
+ let actual = load_from_memory(&encoded_image).unwrap().into_rgba8();
+
+ let diff_image = get_diff(&ref_image, &actual, threshold);
+
+ if let Some(diff_image) = diff_image {
+ if std::env::var("REPLACE").is_ok() && is_reference {
+ write_ref_image();
+ panic!("test was replaced");
+ }
+
+ if !DIFFS_PATH.exists() {
+ let _ = std::fs::create_dir_all(DIFFS_PATH.as_path());
+ }
+
+ let diff_path = DIFFS_PATH.join(format!("{}.png", specific_name));
+ diff_image
+ .save_with_format(&diff_path, image::ImageFormat::Png)
+ .unwrap();
+
+ panic!("test didnt match reference image");
+ }
+ }
+ #[cfg(target_arch = "wasm32")]
+ {
+ assert!(!is_reference, "WASM cannot create new reference images");
+ assert_eq!(test_name.len(), 0, "test_name is not used in wasm32 tests");
+ let actual = load_from_memory(&encoded_image).unwrap().into_rgba8();
+
+ let ref_image = load_from_memory(ref_data).unwrap().into_rgba8();
+
+ let diff_image = get_diff(&ref_image, &actual, threshold);
+ if let Some(ref img) = diff_image {
+ append_diff_image_to_browser_document(specific_name, img);
+ panic!("test didn't match reference image. Scroll to bottom of browser to view diff.");
+ }
}
}