2022-11-27 06:27:19 +11:00
|
|
|
use crate::pico_svg::PicoSvg;
|
|
|
|
use crate::simple_text::SimpleText;
|
2022-11-26 09:16:56 +11:00
|
|
|
use piet_scene::kurbo::{Affine, BezPath, Ellipse, PathEl, Point, Rect};
|
|
|
|
use piet_scene::*;
|
2022-10-25 08:53:12 +11:00
|
|
|
|
2022-11-26 09:16:56 +11:00
|
|
|
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,
|
2022-10-25 09:17:51 +11:00
|
|
|
);
|
2022-10-25 08:53:12 +11:00
|
|
|
}
|
2022-11-05 07:15:05 +11:00
|
|
|
|
2022-11-26 09:16:56 +11:00
|
|
|
#[allow(unused)]
|
|
|
|
const N_CIRCLES: usize = 0;
|
|
|
|
|
|
|
|
#[allow(unused)]
|
2022-11-05 07:15:05 +11:00
|
|
|
pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
|
|
|
use crate::pico_svg::*;
|
|
|
|
let start = std::time::Instant::now();
|
2022-11-26 09:16:56 +11:00
|
|
|
for item in &svg.items {
|
2022-11-05 07:15:05 +11:00
|
|
|
match item {
|
|
|
|
Item::Fill(fill) => {
|
|
|
|
sb.fill(
|
|
|
|
Fill::NonZero,
|
|
|
|
Affine::IDENTITY,
|
2022-11-24 04:50:28 +11:00
|
|
|
fill.color,
|
2022-11-05 07:15:05 +11:00
|
|
|
None,
|
2022-11-23 06:49:51 +11:00
|
|
|
&fill.path,
|
2022-11-05 07:15:05 +11:00
|
|
|
);
|
|
|
|
}
|
|
|
|
Item::Stroke(stroke) => {
|
|
|
|
sb.stroke(
|
2022-11-23 06:49:51 +11:00
|
|
|
&Stroke::new(stroke.width as f32),
|
2022-11-05 07:15:05 +11:00
|
|
|
Affine::IDENTITY,
|
2022-11-24 04:50:28 +11:00
|
|
|
stroke.color,
|
2022-11-05 07:15:05 +11:00
|
|
|
None,
|
2022-11-23 06:49:51 +11:00
|
|
|
&stroke.path,
|
2022-11-05 07:15:05 +11:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if print_stats {
|
|
|
|
println!("flattening and encoding time: {:?}", start.elapsed());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-26 09:16:56 +11:00
|
|
|
#[allow(unused)]
|
|
|
|
pub fn render_tiger(sb: &mut SceneBuilder, print_stats: bool) {
|
|
|
|
use super::pico_svg::*;
|
|
|
|
let xml_str =
|
2022-11-27 06:27:19 +11:00
|
|
|
std::str::from_utf8(include_bytes!("../../assets/Ghostscript_Tiger.svg")).unwrap();
|
2022-11-26 09:16:56 +11:00
|
|
|
let start = std::time::Instant::now();
|
|
|
|
let svg = PicoSvg::load(xml_str, 6.0).unwrap();
|
|
|
|
if print_stats {
|
|
|
|
println!("parsing time: {:?}", start.elapsed());
|
|
|
|
}
|
|
|
|
render_svg(sb, &svg, print_stats);
|
|
|
|
}
|
|
|
|
|
|
|
|
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, 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, 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();
|
|
|
|
}
|
|
|
|
|
2022-11-19 09:26:26 +11:00
|
|
|
#[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;
|
2022-11-23 06:49:51 +11:00
|
|
|
let transform = Affine::translate((i as f64 * 225., j as f64 * 225.));
|
2022-11-19 09:26:26 +11:00
|
|
|
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
|
2022-11-23 06:49:51 +11:00
|
|
|
let rect = Rect::from_origin_size(Point::new(0., 0.), (200., 200.));
|
2022-11-23 07:17:50 +11:00
|
|
|
let linear = LinearGradient::new((0.0, 0.0), (200.0, 0.0)).stops([Color::BLACK, Color::WHITE]);
|
2022-11-24 04:50:28 +11:00
|
|
|
sb.fill(Fill::NonZero, transform, &linear, None, &rect);
|
2022-11-23 06:49:51 +11:00
|
|
|
const GRADIENTS: &[(f64, f64, Color)] = &[
|
2022-11-19 09:26:26 +11:00
|
|
|
(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;
|
2022-11-23 07:17:50 +11:00
|
|
|
let radial = RadialGradient::new((*x, *y), 100.0).stops([*c, color2]);
|
2022-11-24 04:50:28 +11:00
|
|
|
sb.fill(Fill::NonZero, transform, &radial, None, &rect);
|
2022-11-19 09:26:26 +11:00
|
|
|
}
|
|
|
|
const COLORS: &[Color] = &[
|
|
|
|
Color::rgb8(255, 0, 0),
|
|
|
|
Color::rgb8(0, 255, 0),
|
|
|
|
Color::rgb8(0, 0, 255),
|
|
|
|
];
|
2022-11-24 04:50:28 +11:00
|
|
|
sb.push_layer(Mix::Normal, transform, &rect);
|
2022-11-19 09:26:26 +11:00
|
|
|
for (i, c) in COLORS.iter().enumerate() {
|
2022-11-23 07:17:50 +11:00
|
|
|
let linear = LinearGradient::new((0.0, 0.0), (0.0, 200.0)).stops([Color::WHITE, *c]);
|
2022-11-23 06:49:51 +11:00
|
|
|
sb.push_layer(blend, transform, &rect);
|
2022-11-19 09:26:26 +11:00
|
|
|
// squash the ellipse
|
|
|
|
let a = transform
|
2022-11-23 06:49:51 +11:00
|
|
|
* 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.));
|
2022-11-19 09:26:26 +11:00
|
|
|
sb.fill(
|
|
|
|
Fill::NonZero,
|
|
|
|
a,
|
2022-11-24 04:50:28 +11:00
|
|
|
&linear,
|
2022-11-19 09:26:26 +11:00
|
|
|
None,
|
2022-11-23 06:49:51 +11:00
|
|
|
&Ellipse::new((100., 100.), (90., 90.), 0.),
|
2022-11-19 09:26:26 +11:00
|
|
|
);
|
|
|
|
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
|
|
|
|
}
|
2022-11-26 09:16:56 +11:00
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) {
|
|
|
|
sb.fill(
|
|
|
|
Fill::NonZero,
|
|
|
|
Affine::IDENTITY,
|
|
|
|
&Brush::Solid(Color::rgb8(128, 128, 128)),
|
|
|
|
None,
|
|
|
|
&Rect::from_origin_size(Point::new(0.0, 0.0), (1000.0, 1000.0)),
|
|
|
|
);
|
|
|
|
let text_size = 60.0 + 40.0 * (0.01 * i as f32).sin();
|
|
|
|
let s = "\u{1f600}hello piet-gpu 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)][..],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
]
|
|
|
|
}
|