use crate::pico_svg::PicoSvg; use crate::simple_text::SimpleText; use vello::kurbo::{Affine, BezPath, Ellipse, PathEl, Point, Rect}; use vello::peniko::*; use vello::*; pub fn render_funky_paths(sb: &mut SceneBuilder) { use PathEl::*; let missing_movetos = [ LineTo((100.0, 100.0).into()), LineTo((100.0, 200.0).into()), ClosePath, LineTo((0.0, 400.0).into()), LineTo((100.0, 400.0).into()), ]; let only_movetos = [MoveTo((0.0, 0.0).into()), MoveTo((100.0, 100.0).into())]; let empty: [PathEl; 0] = []; sb.fill( Fill::NonZero, Affine::translate((100.0, 100.0)), Color::rgb8(0, 0, 255), None, &missing_movetos, ); sb.fill( Fill::NonZero, Affine::IDENTITY, Color::rgb8(0, 0, 255), None, &empty, ); sb.fill( Fill::NonZero, Affine::IDENTITY, Color::rgb8(0, 0, 255), None, &only_movetos, ); sb.stroke( &Stroke::new(8.0), Affine::translate((100.0, 100.0)), Color::rgb8(0, 255, 255), None, &missing_movetos, ); } #[allow(unused)] const N_CIRCLES: usize = 0; #[allow(unused)] pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg) { use crate::pico_svg::*; for item in &svg.items { match item { Item::Fill(fill) => { sb.fill( Fill::NonZero, Affine::IDENTITY, fill.color, None, &fill.path, ); } Item::Stroke(stroke) => { sb.stroke( &Stroke::new(stroke.width as f32), Affine::IDENTITY, stroke.color, None, &stroke.path, ); } } } } #[allow(unused)] pub fn render_tiger(sb: &mut SceneBuilder) { use super::pico_svg::*; let xml_str = std::str::from_utf8(include_bytes!("../../assets/Ghostscript_Tiger.svg")).unwrap(); let svg = PicoSvg::load(xml_str, 6.0).unwrap(); render_svg(sb, &svg); } pub fn render_scene(sb: &mut SceneBuilder) { render_cardioid(sb); render_clip_test(sb); render_alpha_test(sb); //render_tiger(sb, false); } #[allow(unused)] fn render_cardioid(sb: &mut SceneBuilder) { let n = 601; let dth = std::f64::consts::PI * 2.0 / (n as f64); let center = Point::new(1024.0, 768.0); let r = 750.0; let mut path = BezPath::new(); for i in 1..n { let mut p0 = center; let a0 = i as f64 * dth; p0.x += a0.cos() * r; p0.y += a0.sin() * r; let mut p1 = center; let a1 = ((i * 2) % n) as f64 * dth; p1.x += a1.cos() * r; p1.y += a1.sin() * r; path.push(PathEl::MoveTo(p0)); path.push(PathEl::LineTo(p1)); } sb.stroke( &Stroke::new(2.0), Affine::IDENTITY, Color::rgb8(0, 0, 0), None, &path, ); } #[allow(unused)] fn render_clip_test(sb: &mut SceneBuilder) { const N: usize = 16; const X0: f64 = 50.0; const Y0: f64 = 450.0; // Note: if it gets much larger, it will exceed the 1MB scratch buffer. // But this is a pretty demanding test. const X1: f64 = 550.0; const Y1: f64 = 950.0; let step = 1.0 / ((N + 1) as f64); for i in 0..N { let t = ((i + 1) as f64) * step; let path = [ PathEl::MoveTo((X0, Y0).into()), PathEl::LineTo((X1, Y0).into()), PathEl::LineTo((X1, Y0 + t * (Y1 - Y0)).into()), PathEl::LineTo((X1 + t * (X0 - X1), Y1).into()), PathEl::LineTo((X0, Y1).into()), PathEl::ClosePath, ]; sb.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &path); } let rect = Rect::new(X0, Y0, X1, Y1); sb.fill( Fill::NonZero, Affine::IDENTITY, &Brush::Solid(Color::rgb8(0, 0, 0)), None, &rect, ); for _ in 0..N { sb.pop_layer(); } } #[allow(unused)] fn render_alpha_test(sb: &mut SceneBuilder) { // Alpha compositing tests. sb.fill( Fill::NonZero, Affine::IDENTITY, Color::rgb8(255, 0, 0), None, &make_diamond(1024.0, 100.0), ); sb.fill( Fill::NonZero, Affine::IDENTITY, Color::rgba8(0, 255, 0, 0x80), None, &make_diamond(1024.0, 125.0), ); sb.push_layer( Mix::Clip, 1.0, Affine::IDENTITY, &make_diamond(1024.0, 150.0), ); sb.fill( Fill::NonZero, Affine::IDENTITY, Color::rgba8(0, 0, 255, 0x80), None, &make_diamond(1024.0, 175.0), ); sb.pop_layer(); } #[allow(unused)] pub fn render_blend_grid(sb: &mut SceneBuilder) { const BLEND_MODES: &[Mix] = &[ Mix::Normal, Mix::Multiply, Mix::Darken, Mix::Screen, Mix::Lighten, Mix::Overlay, Mix::ColorDodge, Mix::ColorBurn, Mix::HardLight, Mix::SoftLight, Mix::Difference, Mix::Exclusion, Mix::Hue, Mix::Saturation, Mix::Color, Mix::Luminosity, ]; for (ix, &blend) in BLEND_MODES.iter().enumerate() { let i = ix % 4; let j = ix / 4; let transform = Affine::translate((i as f64 * 225., j as f64 * 225.)); let square = blend_square(blend.into()); sb.append(&square, Some(transform)); } } #[allow(unused)] fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affine) { // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode let rect = Rect::from_origin_size(Point::new(0., 0.), (200., 200.)); let linear = LinearGradient::new((0.0, 0.0), (200.0, 0.0)).stops([Color::BLACK, Color::WHITE]); sb.fill(Fill::NonZero, transform, &linear, None, &rect); const GRADIENTS: &[(f64, f64, Color)] = &[ (150., 0., Color::rgb8(255, 240, 64)), (175., 100., Color::rgb8(255, 96, 240)), (125., 200., Color::rgb8(64, 192, 255)), ]; for (x, y, c) in GRADIENTS { let mut color2 = c.clone(); color2.a = 0; let radial = RadialGradient::new((*x, *y), 100.0).stops([*c, color2]); sb.fill(Fill::NonZero, transform, &radial, None, &rect); } const COLORS: &[Color] = &[ Color::rgb8(255, 0, 0), Color::rgb8(0, 255, 0), Color::rgb8(0, 0, 255), ]; sb.push_layer(Mix::Normal, 1.0, transform, &rect); for (i, c) in COLORS.iter().enumerate() { let linear = LinearGradient::new((0.0, 0.0), (0.0, 200.0)).stops([Color::WHITE, *c]); sb.push_layer(blend, 1.0, transform, &rect); // squash the ellipse let a = transform * Affine::translate((100., 100.)) * Affine::rotate(std::f64::consts::FRAC_PI_3 * (i * 2 + 1) as f64) * Affine::scale_non_uniform(1.0, 0.357) * Affine::translate((-100., -100.)); sb.fill( Fill::NonZero, a, &linear, None, &Ellipse::new((100., 100.), (90., 90.), 0.), ); sb.pop_layer(); } sb.pop_layer(); } #[allow(unused)] fn blend_square(blend: BlendMode) -> SceneFragment { let mut fragment = SceneFragment::default(); let mut sb = SceneBuilder::for_fragment(&mut fragment); render_blend_square(&mut sb, blend, Affine::IDENTITY); sb.finish(); fragment } #[allow(unused)] pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) { use PathEl::*; let rect = Rect::from_origin_size(Point::new(0.0, 0.0), (1000.0, 1000.0)); let star = [ MoveTo((50.0, 0.0).into()), LineTo((21.0, 90.0).into()), LineTo((98.0, 35.0).into()), LineTo((2.0, 35.0).into()), LineTo((79.0, 90.0).into()), ClosePath, ]; sb.fill( Fill::NonZero, Affine::IDENTITY, &Brush::Solid(Color::rgb8(128, 128, 128)), None, &rect, ); let text_size = 60.0 + 40.0 * (0.01 * i as f32).sin(); let s = "\u{1f600}hello vello text!"; text.add( sb, None, text_size, None, Affine::translate((110.0, 600.0)), s, ); text.add( sb, None, text_size, None, Affine::translate((110.0, 700.0)), s, ); let th = (std::f64::consts::PI / 180.0) * (i as f64); let center = Point::new(500.0, 500.0); let mut p1 = center; p1.x += 400.0 * th.cos(); p1.y += 400.0 * th.sin(); sb.stroke( &Stroke::new(5.0), Affine::IDENTITY, &Brush::Solid(Color::rgb8(128, 0, 0)), None, &[PathEl::MoveTo(center), PathEl::LineTo(p1)], ); sb.fill( Fill::NonZero, Affine::translate((150.0, 150.0)) * Affine::scale(0.2), Color::RED, None, &rect, ); let alpha = (i as f64 * 0.03).sin() as f32 * 0.5 + 0.5; sb.push_layer(Mix::Normal, alpha, Affine::IDENTITY, &rect); sb.fill( Fill::NonZero, Affine::translate((100.0, 100.0)) * Affine::scale(0.2), Color::BLUE, None, &rect, ); sb.fill( Fill::NonZero, Affine::translate((200.0, 200.0)) * Affine::scale(0.2), Color::GREEN, None, &rect, ); sb.pop_layer(); sb.fill( Fill::NonZero, Affine::translate((400.0, 100.0)), Color::PURPLE, None, &star, ); sb.fill( Fill::EvenOdd, Affine::translate((500.0, 100.0)), Color::PURPLE, None, &star, ); } #[allow(unused)] pub fn render_brush_transform(sb: &mut SceneBuilder, i: usize) { let th = (std::f64::consts::PI / 180.0) * (i as f64); let linear = LinearGradient::new((0.0, 0.0), (0.0, 200.0)).stops([ Color::RED, Color::GREEN, Color::BLUE, ]); sb.fill( Fill::NonZero, Affine::translate((200.0, 200.0)), &linear, Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))), &Rect::from_origin_size(Point::default(), (400.0, 200.0)), ); sb.stroke( &Stroke::new(40.0), Affine::translate((800.0, 200.0)), &linear, Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))), &Rect::from_origin_size(Point::default(), (400.0, 200.0)), ); } fn around_center(xform: Affine, center: Point) -> Affine { Affine::translate(center.to_vec2()) * xform * Affine::translate(-center.to_vec2()) } fn make_diamond(cx: f64, cy: f64) -> [PathEl; 5] { const SIZE: f64 = 50.0; [ PathEl::MoveTo(Point::new(cx, cy - SIZE)), PathEl::LineTo(Point::new(cx + SIZE, cy)), PathEl::LineTo(Point::new(cx, cy + SIZE)), PathEl::LineTo(Point::new(cx - SIZE, cy)), PathEl::ClosePath, ] }