2022-10-24 14:53:12 -07:00
|
|
|
// Copyright 2022 Google LLC
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// Also licensed under MIT license, at your choice.
|
|
|
|
|
2022-11-04 13:15:05 -07:00
|
|
|
use kurbo::BezPath;
|
2022-11-16 12:02:11 -05:00
|
|
|
use piet_scene::{
|
2022-11-18 17:26:26 -05:00
|
|
|
Affine, BlendMode, Brush, Color, Compose, ExtendMode, Fill, GradientStop, LinearGradient, Mix,
|
|
|
|
PathElement, Point, RadialGradient, Rect, Scene, SceneBuilder, SceneFragment, Stroke,
|
2022-11-16 12:02:11 -05:00
|
|
|
};
|
2022-10-24 14:53:12 -07:00
|
|
|
|
2022-11-04 13:15:05 -07:00
|
|
|
use crate::pico_svg::PicoSvg;
|
|
|
|
|
2022-10-24 14:53:12 -07:00
|
|
|
pub fn gen_test_scene() -> Scene {
|
|
|
|
let mut scene = Scene::default();
|
|
|
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
2022-11-16 10:49:38 -05:00
|
|
|
let scene_ix = 1;
|
|
|
|
match scene_ix {
|
|
|
|
0 => {
|
|
|
|
let path = [
|
|
|
|
PathElement::MoveTo(Point::new(100.0, 100.0)),
|
|
|
|
PathElement::LineTo(Point::new(500.0, 120.0)),
|
|
|
|
PathElement::LineTo(Point::new(300.0, 150.0)),
|
|
|
|
PathElement::LineTo(Point::new(200.0, 260.0)),
|
|
|
|
PathElement::LineTo(Point::new(150.0, 210.0)),
|
|
|
|
PathElement::Close,
|
|
|
|
];
|
|
|
|
let brush = Brush::Solid(Color::rgb8(0x40, 0x40, 0xff));
|
|
|
|
builder.fill(Fill::NonZero, Affine::IDENTITY, &brush, None, &path);
|
|
|
|
let transform = Affine::translate(50.0, 50.0);
|
|
|
|
let brush = Brush::Solid(Color::rgba8(0xff, 0xff, 0x00, 0x80));
|
|
|
|
builder.fill(Fill::NonZero, transform, &brush, None, &path);
|
|
|
|
let transform = Affine::translate(100.0, 100.0);
|
|
|
|
let style = simple_stroke(1.0);
|
|
|
|
let brush = Brush::Solid(Color::rgb8(0xa0, 0x00, 0x00));
|
|
|
|
builder.stroke(&style, transform, &brush, None, &path);
|
|
|
|
}
|
|
|
|
1 => {
|
2022-11-18 17:26:26 -05:00
|
|
|
render_blend_grid(&mut builder);
|
2022-11-16 10:49:38 -05:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let xml_str =
|
|
|
|
std::str::from_utf8(include_bytes!("../../piet-gpu/Ghostscript_Tiger.svg"))
|
|
|
|
.unwrap();
|
|
|
|
let svg = PicoSvg::load(xml_str, 6.0).unwrap();
|
|
|
|
render_svg(&mut builder, &svg, false);
|
|
|
|
}
|
2022-11-04 13:15:05 -07:00
|
|
|
}
|
2022-11-18 17:26:26 -05:00
|
|
|
builder.finish();
|
2022-10-24 14:53:12 -07:00
|
|
|
scene
|
|
|
|
}
|
|
|
|
|
2022-11-04 21:41:37 -07:00
|
|
|
#[allow(unused)]
|
2022-10-24 14:53:12 -07:00
|
|
|
pub fn dump_scene_info(scene: &Scene) {
|
|
|
|
let data = scene.data();
|
|
|
|
println!("tags {:?}", data.tag_stream);
|
2022-10-24 15:17:51 -07:00
|
|
|
println!(
|
|
|
|
"pathsegs {:?}",
|
|
|
|
bytemuck::cast_slice::<u8, f32>(&data.pathseg_stream)
|
|
|
|
);
|
2022-10-24 14:53:12 -07:00
|
|
|
}
|
2022-11-04 13:15:05 -07:00
|
|
|
|
|
|
|
pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
|
|
|
use crate::pico_svg::*;
|
|
|
|
let start = std::time::Instant::now();
|
|
|
|
for item in svg.items.iter() {
|
|
|
|
match item {
|
|
|
|
Item::Fill(fill) => {
|
|
|
|
sb.fill(
|
|
|
|
Fill::NonZero,
|
|
|
|
Affine::IDENTITY,
|
|
|
|
&fill.color.into(),
|
|
|
|
None,
|
|
|
|
convert_bez_path(&fill.path),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Item::Stroke(stroke) => {
|
|
|
|
sb.stroke(
|
|
|
|
&simple_stroke(stroke.width as f32),
|
|
|
|
Affine::IDENTITY,
|
|
|
|
&stroke.color.into(),
|
|
|
|
None,
|
|
|
|
convert_bez_path(&stroke.path),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if print_stats {
|
|
|
|
println!("flattening and encoding time: {:?}", start.elapsed());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator<Item = PathElement> + 'a + Clone {
|
|
|
|
path.elements()
|
|
|
|
.iter()
|
|
|
|
.map(|el| PathElement::from_kurbo(*el))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn simple_stroke(width: f32) -> Stroke<[f32; 0]> {
|
|
|
|
Stroke {
|
|
|
|
width,
|
|
|
|
join: piet_scene::Join::Round,
|
|
|
|
miter_limit: 1.4,
|
|
|
|
start_cap: piet_scene::Cap::Round,
|
|
|
|
end_cap: piet_scene::Cap::Round,
|
|
|
|
dash_pattern: [],
|
|
|
|
dash_offset: 0.0,
|
|
|
|
scale: true,
|
|
|
|
}
|
|
|
|
}
|
2022-11-18 17:26:26 -05: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;
|
|
|
|
let transform = Affine::translate(i as f32 * 225., j as f32 * 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 stops = &[
|
|
|
|
GradientStop {
|
|
|
|
color: Color::rgb8(0, 0, 0),
|
|
|
|
offset: 0.0,
|
|
|
|
},
|
|
|
|
GradientStop {
|
|
|
|
color: Color::rgb8(255, 255, 255),
|
|
|
|
offset: 1.0,
|
|
|
|
},
|
|
|
|
][..];
|
|
|
|
let linear = Brush::LinearGradient(LinearGradient {
|
|
|
|
start: Point::new(0.0, 0.0),
|
|
|
|
end: Point::new(200.0, 0.0),
|
|
|
|
stops: stops.into(),
|
|
|
|
extend: ExtendMode::Pad,
|
|
|
|
});
|
|
|
|
sb.fill(Fill::NonZero, transform, &linear, None, rect.elements());
|
|
|
|
const GRADIENTS: &[(f32, f32, 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 stops = &[
|
|
|
|
GradientStop {
|
|
|
|
color: c.clone(),
|
|
|
|
offset: 0.0,
|
|
|
|
},
|
|
|
|
GradientStop {
|
|
|
|
color: color2,
|
|
|
|
offset: 1.0,
|
|
|
|
},
|
|
|
|
][..];
|
|
|
|
let rad = Brush::RadialGradient(RadialGradient {
|
|
|
|
center0: Point::new(*x, *y),
|
|
|
|
center1: Point::new(*x, *y),
|
|
|
|
radius0: 0.0,
|
|
|
|
radius1: 100.0,
|
|
|
|
stops: stops.into(),
|
|
|
|
extend: ExtendMode::Pad,
|
|
|
|
});
|
|
|
|
sb.fill(Fill::NonZero, transform, &rad, None, rect.elements());
|
|
|
|
}
|
|
|
|
const COLORS: &[Color] = &[
|
|
|
|
Color::rgb8(255, 0, 0),
|
|
|
|
Color::rgb8(0, 255, 0),
|
|
|
|
Color::rgb8(0, 0, 255),
|
|
|
|
];
|
|
|
|
sb.push_layer(Mix::Normal.into(), transform, rect.elements());
|
|
|
|
for (i, c) in COLORS.iter().enumerate() {
|
|
|
|
let stops = &[
|
|
|
|
GradientStop {
|
|
|
|
color: Color::rgb8(255, 255, 255),
|
|
|
|
offset: 0.0,
|
|
|
|
},
|
|
|
|
GradientStop {
|
|
|
|
color: c.clone(),
|
|
|
|
offset: 1.0,
|
|
|
|
},
|
|
|
|
][..];
|
|
|
|
let linear = Brush::LinearGradient(LinearGradient {
|
|
|
|
start: Point::new(0.0, 0.0),
|
|
|
|
end: Point::new(0.0, 200.0),
|
|
|
|
stops: stops.into(),
|
|
|
|
extend: ExtendMode::Pad,
|
|
|
|
});
|
|
|
|
sb.push_layer(blend, transform, rect.elements());
|
|
|
|
// squash the ellipse
|
|
|
|
let a = transform
|
|
|
|
* Affine::translate(100., 100.)
|
|
|
|
* Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32)
|
|
|
|
* Affine::scale(1.0, 0.357)
|
|
|
|
* Affine::translate(-100., -100.);
|
|
|
|
sb.fill(
|
|
|
|
Fill::NonZero,
|
|
|
|
a,
|
|
|
|
&linear,
|
|
|
|
None,
|
|
|
|
make_ellipse(100., 100., 90., 90.),
|
|
|
|
);
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_ellipse(cx: f32, cy: f32, rx: f32, ry: f32) -> impl Iterator<Item = PathElement> + Clone {
|
|
|
|
let a = 0.551915024494;
|
|
|
|
let arx = a * rx;
|
|
|
|
let ary = a * ry;
|
|
|
|
let elements = [
|
|
|
|
PathElement::MoveTo(Point::new(cx + rx, cy)),
|
|
|
|
PathElement::CurveTo(
|
|
|
|
Point::new(cx + rx, cy + ary),
|
|
|
|
Point::new(cx + arx, cy + ry),
|
|
|
|
Point::new(cx, cy + ry),
|
|
|
|
),
|
|
|
|
PathElement::CurveTo(
|
|
|
|
Point::new(cx - arx, cy + ry),
|
|
|
|
Point::new(cx - rx, cy + ary),
|
|
|
|
Point::new(cx - rx, cy),
|
|
|
|
),
|
|
|
|
PathElement::CurveTo(
|
|
|
|
Point::new(cx - rx, cy - ary),
|
|
|
|
Point::new(cx - arx, cy - ry),
|
|
|
|
Point::new(cx, cy - ry),
|
|
|
|
),
|
|
|
|
PathElement::CurveTo(
|
|
|
|
Point::new(cx + arx, cy - ry),
|
|
|
|
Point::new(cx + rx, cy - ary),
|
|
|
|
Point::new(cx + rx, cy),
|
|
|
|
),
|
|
|
|
PathElement::Close,
|
|
|
|
];
|
|
|
|
(0..elements.len()).map(move |i| elements[i])
|
|
|
|
}
|