Merge pull request #387 from Zoxc/wasm-fix
Fix shader compilation on Chrome
diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs
index 48d70d3..982076f 100644
--- a/examples/scenes/src/test_scenes.rs
+++ b/examples/scenes/src/test_scenes.rs
@@ -1,5 +1,8 @@
+// Copyright 2022 The Vello authors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::{ExampleScene, SceneConfig, SceneParams, SceneSet};
-use vello::kurbo::{Affine, BezPath, Ellipse, PathEl, Point, Rect, Stroke};
+use vello::kurbo::{Affine, BezPath, Cap, Ellipse, Join, PathEl, Point, Rect, Stroke};
use vello::peniko::*;
use vello::*;
@@ -13,35 +16,26 @@
scene!($name: true)
};
($name: ident: $animated: literal) => {
+ scene!($name, stringify!($name), $animated)
+ };
+ ($func:expr, $name: expr, $animated: literal) => {
ExampleScene {
config: SceneConfig {
animated: $animated,
- name: stringify!($name).to_owned(),
+ name: $name.to_owned(),
},
- function: Box::new($name),
+ function: Box::new($func),
}
};
}
pub fn test_scenes() -> SceneSet {
- let splash_scene = ExampleScene {
- config: SceneConfig {
- animated: false,
- name: "splash_with_tiger".to_owned(),
- },
- function: Box::new(splash_with_tiger()),
- };
- let mmark_scene = ExampleScene {
- config: SceneConfig {
- animated: false,
- name: "mmark".to_owned(),
- },
- function: Box::new(crate::mmark::MMark::new(80_000)),
- };
let scenes = vec![
- splash_scene,
- mmark_scene,
+ scene!(splash_with_tiger(), "splash_with_tiger", false),
+ scene!(crate::mmark::MMark::new(80_000), "mmark", false),
scene!(funky_paths),
+ scene!(stroke_styles),
+ scene!(tricky_strokes),
scene!(cardioid_and_friends),
scene!(animated_text: animated),
scene!(gradient_extend),
@@ -52,6 +46,8 @@
scene!(labyrinth),
scene!(base_color_test: animated),
scene!(clip_test: animated),
+ scene!(longpathdash(Cap::Butt), "longpathdash (butt caps)", false),
+ scene!(longpathdash(Cap::Round), "longpathdash (round caps)", false),
];
SceneSet { scenes }
@@ -100,6 +96,197 @@
);
}
+fn stroke_styles(sb: &mut SceneBuilder, params: &mut SceneParams) {
+ use PathEl::*;
+ let colors = [
+ Color::rgb8(140, 181, 236),
+ Color::rgb8(246, 236, 202),
+ Color::rgb8(201, 147, 206),
+ Color::rgb8(150, 195, 160),
+ ];
+ let simple_stroke = [LineTo((100., 0.).into())];
+ let join_stroke = [
+ CurveTo((20., 0.).into(), (42.5, 5.).into(), (50., 25.).into()),
+ CurveTo((57.5, 5.).into(), (80., 0.).into(), (100., 0.).into()),
+ ];
+ let miter_stroke = [LineTo((90., 21.).into()), LineTo((0., 42.).into())];
+ let cap_styles = [Cap::Butt, Cap::Square, Cap::Round];
+ let join_styles = [Join::Bevel, Join::Miter, Join::Round];
+ let miter_limits = [4., 5., 0.1, 10.];
+
+ // Simple strokes with cap combinations
+ let t = Affine::translate((60., 40.)) * Affine::scale(2.);
+ let mut y = 0.;
+ let mut color_idx = 0;
+ for start in cap_styles {
+ for end in cap_styles {
+ params.text.add(
+ sb,
+ None,
+ 12.,
+ None,
+ Affine::translate((0., y)) * t,
+ &format!("Start cap: {:?}, End cap: {:?}", start, end),
+ );
+ sb.stroke(
+ &Stroke::new(20.).with_start_cap(start).with_end_cap(end),
+ Affine::translate((0., y + 30.)) * t,
+ colors[color_idx],
+ None,
+ &simple_stroke,
+ );
+ y += 180.;
+ color_idx = (color_idx + 1) % colors.len();
+ }
+ }
+
+ // Cap and join combinations
+ let t = Affine::translate((500., 0.)) * t;
+ y = 0.;
+ for cap in cap_styles {
+ for join in join_styles {
+ params.text.add(
+ sb,
+ None,
+ 12.,
+ None,
+ Affine::translate((0., y)) * t,
+ &format!("Caps: {:?}, Joins: {:?}", cap, join),
+ );
+ sb.stroke(
+ &Stroke::new(20.).with_caps(cap).with_join(join),
+ Affine::translate((0., y + 30.)) * t,
+ colors[color_idx],
+ None,
+ &join_stroke,
+ );
+ y += 185.;
+ color_idx = (color_idx + 1) % colors.len();
+ }
+ }
+
+ // Miter limit
+ let t = Affine::translate((500., 0.)) * t;
+ y = 0.;
+ for ml in miter_limits {
+ params.text.add(
+ sb,
+ None,
+ 12.,
+ None,
+ Affine::translate((0., y)) * t,
+ &format!("Miter limit: {}", ml),
+ );
+ sb.stroke(
+ &Stroke::new(10.)
+ .with_caps(Cap::Butt)
+ .with_join(Join::Miter)
+ .with_miter_limit(ml),
+ Affine::translate((0., y + 30.)) * t,
+ colors[color_idx],
+ None,
+ &miter_stroke,
+ );
+ y += 180.;
+ color_idx = (color_idx + 1) % colors.len();
+ }
+}
+
+// This test has been adapted from Skia's "trickycubicstrokes" GM slide which can be found at
+// `github.com/google/skia/blob/0d4d11451c4f4e184305cbdbd67f6b3edfa4b0e3/gm/trickycubicstrokes.cpp`
+fn tricky_strokes(sb: &mut SceneBuilder, _: &mut SceneParams) {
+ use PathEl::*;
+ let colors = [
+ Color::rgb8(140, 181, 236),
+ Color::rgb8(246, 236, 202),
+ Color::rgb8(201, 147, 206),
+ Color::rgb8(150, 195, 160),
+ ];
+
+ const CELL_SIZE: f64 = 200.;
+ const STROKE_WIDTH: f64 = 30.;
+ const NUM_COLS: usize = 5;
+
+ fn stroke_bounds(pts: &[(f64, f64); 4]) -> Rect {
+ use kurbo::{CubicBez, Shape};
+ CubicBez::new(pts[0], pts[1], pts[2], pts[3])
+ .bounding_box()
+ .inflate(STROKE_WIDTH, STROKE_WIDTH)
+ }
+
+ fn map_rect_to_rect(src: &Rect, dst: &Rect) -> (Affine, f64) {
+ let (scale, x_larger) = {
+ let sx = dst.width() / src.width();
+ let sy = dst.height() / src.height();
+ (sx.min(sy), sx > sy)
+ };
+ let tx = dst.x0 - src.x0 * scale;
+ let ty = dst.y0 - src.y0 * scale;
+ let (tx, ty) = if x_larger {
+ (tx + 0.5 * (dst.width() - src.width() * scale), ty)
+ } else {
+ (tx, ty + 0.5 * (dst.height() - src.height() * scale))
+ };
+ (Affine::new([scale, 0.0, 0.0, scale, tx, ty]), scale)
+ }
+
+ let tricky_cubics = [
+ [(122., 737.), (348., 553.), (403., 761.), (400., 760.)],
+ [(244., 520.), (244., 518.), (1141., 634.), (394., 688.)],
+ [(550., 194.), (138., 130.), (1035., 246.), (288., 300.)],
+ [(226., 733.), (556., 779.), (-43., 471.), (348., 683.)],
+ [(268., 204.), (492., 304.), (352., 23.), (433., 412.)],
+ [(172., 480.), (396., 580.), (256., 299.), (338., 677.)],
+ [(731., 340.), (318., 252.), (1026., -64.), (367., 265.)],
+ [(475., 708.), (62., 620.), (770., 304.), (220., 659.)],
+ [(0., 0.), (128., 128.), (128., 0.), (0., 128.)], // Perfect cusp
+ [(0., 0.01), (128., 127.999), (128., 0.01), (0., 127.99)], // Near-cusp
+ ];
+
+ // FIXME: The following curves all cause a crash due to a stack overflow following an
+ // infinite recursion in `kurbo::fit::fit_to_bezpath_rec` which gets called by
+ // `SceneBuilder::stroke` below. Disabling these tests until kurbo handles these
+ // gracefully. Move these into `tricky_cubics` above once they are fixed.
+ let _broken_cubics = [
+ [(0., -0.01), (128., 128.001), (128., -0.01), (0., 128.001)], // Near-cusp
+ [(0., 0.), (0., -10.), (0., -10.), (0., 10.)], // Flat line with 180
+ [(10., 0.), (0., 0.), (20., 0.), (10., 0.)], // Flat line with 2 180s
+ [(39., -39.), (40., -40.), (40., -40.), (0., 0.)], // Flat diagonal with 180
+ [(40., 40.), (0., 0.), (200., 200.), (0., 0.)], // Diag w/ an internal 180
+ [(0., 0.), (1e-2, 0.), (-1e-2, 0.), (0., 0.)], // Circle
+ // Flat line with no turns:
+ [
+ (400.75, 100.05),
+ (400.75, 100.05),
+ (100.05, 300.95),
+ (100.05, 300.95),
+ ],
+ [(0.5, 0.), (0., 0.), (20., 0.), (10., 0.)], // Flat line with 2 180s
+ [(10., 0.), (0., 0.), (10., 0.), (10., 0.)], // Flat line with a 180
+ ];
+ let mut color_idx = 0;
+ for (i, cubic) in tricky_cubics.into_iter().enumerate() {
+ let x = (i % NUM_COLS) as f64 * CELL_SIZE;
+ let y = (i / NUM_COLS) as f64 * CELL_SIZE;
+ let cell = Rect::new(x, y, x + CELL_SIZE, y + CELL_SIZE);
+ let bounds = stroke_bounds(&cubic);
+ let (t, s) = map_rect_to_rect(&bounds, &cell);
+ sb.stroke(
+ &Stroke::new(STROKE_WIDTH / s)
+ .with_caps(Cap::Butt)
+ .with_join(Join::Miter),
+ t,
+ colors[color_idx],
+ None,
+ &[
+ MoveTo(cubic[0].into()),
+ CurveTo(cubic[1].into(), cubic[2].into(), cubic[3].into()),
+ ],
+ );
+ color_idx = (color_idx + 1) % colors.len();
+ }
+}
+
fn cardioid_and_friends(sb: &mut SceneBuilder, _: &mut SceneParams) {
render_cardioid(sb);
render_clip_test(sb);
@@ -107,6 +294,48 @@
//render_tiger(sb, false);
}
+fn longpathdash(cap: Cap) -> impl FnMut(&mut SceneBuilder, &mut SceneParams) {
+ use std::f64::consts::PI;
+ use PathEl::*;
+ move |sb, _| {
+ let mut path = BezPath::new();
+ let mut x = 32;
+ while x < 256 {
+ let mut a: f64 = 0.0;
+ while a < PI * 2.0 {
+ let pts = [
+ (256.0 + a.sin() * x as f64, 256.0 + a.cos() * x as f64),
+ (
+ 256.0 + (a + PI / 3.0).sin() * (x + 64) as f64,
+ 256.0 + (a + PI / 3.0).cos() * (x + 64) as f64,
+ ),
+ ];
+ path.push(MoveTo(pts[0].into()));
+ let mut i: f64 = 0.0;
+ while i < 1.0 {
+ path.push(LineTo(
+ (
+ pts[0].0 * (1.0 - i) + pts[1].0 * i,
+ pts[0].1 * (1.0 - i) + pts[1].1 * i,
+ )
+ .into(),
+ ));
+ i += 0.05;
+ }
+ a += PI * 0.01;
+ }
+ x += 16;
+ }
+ sb.stroke(
+ &Stroke::new(1.0).with_caps(cap).with_dashes(0.0, [1.0, 1.0]),
+ Affine::translate((50.0, 50.0)),
+ Color::rgb8(255, 255, 0),
+ None,
+ &path,
+ );
+ }
+}
+
fn animated_text(sb: &mut SceneBuilder, params: &mut SceneParams) {
// Uses the static array address as a cache key for expedience. Real code
// should use a better strategy.