mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-10 12:41:30 +11:00
Merge pull request #208 from dfrg/peniko
Update piet-scene to depend on peniko
This commit is contained in:
commit
5dbeb992e9
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -503,9 +503,9 @@ checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
|||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b6bdbb8c5a42b2bb5ee8dd9dc2c7d73ce3e15d26dfe100fb347ffa3f58c672b"
|
||||
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
|
@ -710,9 +710,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "kurbo"
|
||||
version = "0.8.3"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
|
||||
checksum = "e119590a03caff1f7a582e8ee8c2164ddcc975791701188132fd1d1b518d3871"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
]
|
||||
|
@ -1068,6 +1068,15 @@ dependencies = [
|
|||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peniko"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/linebender/peniko#b83821720aa51a3942be5d20c71525a1ae61ac0a"
|
||||
dependencies = [
|
||||
"kurbo 0.9.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
|
@ -1093,7 +1102,6 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bytemuck",
|
||||
"clap 3.2.23",
|
||||
"kurbo 0.8.3",
|
||||
"ndk 0.3.0",
|
||||
"ndk-glue 0.3.0",
|
||||
"ndk-sys 0.2.2",
|
||||
|
@ -1162,8 +1170,8 @@ name = "piet-scene"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"kurbo 0.8.3",
|
||||
"moscato",
|
||||
"peniko",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
|
@ -1174,7 +1182,6 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"env_logger",
|
||||
"futures-intrusive",
|
||||
"kurbo 0.8.3",
|
||||
"parking_lot",
|
||||
"piet-scene",
|
||||
"png",
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
mod render;
|
||||
|
||||
use piet_scene::{Brush, Color, Fill, PathElement};
|
||||
use piet_scene::kurbo::{Affine, PathEl, Point};
|
||||
use piet_scene::{Brush, Color, Fill};
|
||||
use render::*;
|
||||
use std::ffi::c_void;
|
||||
use std::mem::transmute;
|
||||
|
@ -145,7 +146,7 @@ pub struct PgpuPathElement {
|
|||
pub points: [PgpuPoint; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct PgpuPathIter {
|
||||
pub context: *mut c_void,
|
||||
|
@ -197,16 +198,16 @@ pub struct PgpuTransform {
|
|||
pub dy: f32,
|
||||
}
|
||||
|
||||
impl From<PgpuTransform> for piet_scene::Affine {
|
||||
impl From<PgpuTransform> for Affine {
|
||||
fn from(xform: PgpuTransform) -> Self {
|
||||
Self {
|
||||
xx: xform.xx,
|
||||
yx: xform.yx,
|
||||
xy: xform.xy,
|
||||
yy: xform.yy,
|
||||
dx: xform.dx,
|
||||
dy: xform.dy,
|
||||
}
|
||||
Affine::new([
|
||||
xform.xx as f64,
|
||||
xform.yx as f64,
|
||||
xform.xy as f64,
|
||||
xform.yy as f64,
|
||||
xform.dx as f64,
|
||||
xform.dy as f64,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,27 +240,26 @@ pub unsafe extern "C" fn pgpu_scene_builder_add_glyph(
|
|||
}
|
||||
|
||||
impl Iterator for PgpuPathIter {
|
||||
type Item = PathElement;
|
||||
type Item = PathEl;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut el = PgpuPathElement {
|
||||
verb: PgpuPathVerb::MoveTo,
|
||||
points: [PgpuPoint::default(); 3],
|
||||
};
|
||||
fn conv_pt(pt: PgpuPoint) -> Point {
|
||||
Point::new(pt.x as f64, pt.y as f64)
|
||||
}
|
||||
if (self.next_element)(self.context, &mut el as _) {
|
||||
let p = &el.points;
|
||||
Some(match el.verb {
|
||||
PgpuPathVerb::MoveTo => PathElement::MoveTo((p[0].x, p[0].y).into()),
|
||||
PgpuPathVerb::LineTo => PathElement::LineTo((p[0].x, p[0].y).into()),
|
||||
PgpuPathVerb::QuadTo => {
|
||||
PathElement::QuadTo((p[0].x, p[0].y).into(), (p[1].x, p[1].y).into())
|
||||
PgpuPathVerb::MoveTo => PathEl::MoveTo(conv_pt(p[0])),
|
||||
PgpuPathVerb::LineTo => PathEl::LineTo(conv_pt(p[0])),
|
||||
PgpuPathVerb::QuadTo => PathEl::QuadTo(conv_pt(p[0]), conv_pt(p[1])),
|
||||
PgpuPathVerb::CurveTo => {
|
||||
PathEl::CurveTo(conv_pt(p[0]), conv_pt(p[1]), conv_pt(p[2]))
|
||||
}
|
||||
PgpuPathVerb::CurveTo => PathElement::CurveTo(
|
||||
(p[0].x, p[0].y).into(),
|
||||
(p[1].x, p[1].y).into(),
|
||||
(p[2].x, p[2].y).into(),
|
||||
),
|
||||
PgpuPathVerb::Close => PathElement::Close,
|
||||
PgpuPathVerb::Close => PathEl::ClosePath,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -308,12 +308,13 @@ pub unsafe extern "C" fn pgpu_scene_builder_fill_path(
|
|||
} else {
|
||||
Some((*brush_transform).into())
|
||||
};
|
||||
let path_els = (*path).collect::<Vec<_>>();
|
||||
(*builder).builder.fill(
|
||||
fill,
|
||||
(*builder).transform,
|
||||
&brush,
|
||||
brush_transform,
|
||||
(*path).clone(),
|
||||
&&path_els[..],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -445,13 +446,14 @@ pub unsafe extern "C" fn pgpu_glyph_bbox(
|
|||
glyph: *const PgpuGlyph,
|
||||
transform: &[f32; 6],
|
||||
) -> PgpuRect {
|
||||
let transform = piet_scene::Affine::new(transform);
|
||||
let transform: PgpuTransform = std::mem::transmute(*transform);
|
||||
let transform = transform.into();
|
||||
let rect = (*glyph).bbox(Some(transform));
|
||||
PgpuRect {
|
||||
x0: rect.min.x,
|
||||
y0: rect.min.y,
|
||||
x1: rect.max.x,
|
||||
y1: rect.max.y,
|
||||
x0: rect.min_x() as f32,
|
||||
y0: rect.min_y() as f32,
|
||||
x1: rect.max_x() as f32,
|
||||
y1: rect.max_y() as f32,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ use piet_gpu::{PixelFormat, RenderConfig};
|
|||
use piet_gpu_hal::{QueryPool, Session};
|
||||
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
|
||||
use piet_scene::glyph::{GlyphContext, GlyphProvider};
|
||||
use piet_scene::{Affine, Rect, Scene, SceneFragment};
|
||||
use piet_scene::kurbo::{Affine, Point, Rect};
|
||||
use piet_scene::{Scene, SceneFragment};
|
||||
|
||||
/// State and resources for rendering a scene.
|
||||
pub struct PgpuRenderer {
|
||||
|
@ -141,7 +142,7 @@ pub struct PgpuSceneBuilder<'a> {
|
|||
}
|
||||
|
||||
impl<'a> PgpuSceneBuilder<'a> {
|
||||
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::Affine) {
|
||||
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &Affine) {
|
||||
self.builder.append(&glyph.fragment, Some(*transform));
|
||||
}
|
||||
|
||||
|
@ -214,15 +215,25 @@ pub struct PgpuGlyph {
|
|||
|
||||
impl PgpuGlyph {
|
||||
pub fn bbox(&self, transform: Option<Affine>) -> Rect {
|
||||
let points = self.fragment.points();
|
||||
if points.is_empty() {
|
||||
return Rect::default();
|
||||
}
|
||||
let mut points = points
|
||||
.iter()
|
||||
.map(|pt| Point::new(pt[0] as f64, pt[1] as f64));
|
||||
if let Some(transform) = &transform {
|
||||
Rect::from_points(
|
||||
self.fragment
|
||||
.points()
|
||||
.iter()
|
||||
.map(|p| p.transform(transform)),
|
||||
)
|
||||
let mut rect = Rect::from_center_size(points.next().unwrap(), (0.0, 0.0));
|
||||
for point in points {
|
||||
rect = rect.union_pt(*transform * point);
|
||||
}
|
||||
rect
|
||||
} else {
|
||||
Rect::from_points(self.fragment.points())
|
||||
let mut rect = Rect::from_center_size(points.next().unwrap(), (0.0, 0.0));
|
||||
for point in points {
|
||||
rect = rect.union_pt(point);
|
||||
}
|
||||
rect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ path = "../piet-gpu-types"
|
|||
|
||||
[dependencies.piet-scene]
|
||||
path = "../piet-scene"
|
||||
features = ["kurbo"]
|
||||
|
||||
[dependencies]
|
||||
png = "0.17.6"
|
||||
|
@ -38,7 +37,6 @@ winit = {version = "0.27.3", default-features = false, features = ["x11", "wayl
|
|||
raw-window-handle = "0.5"
|
||||
clap = "3.2.22"
|
||||
bytemuck = { version = "1.7.2", features = ["derive"] }
|
||||
kurbo = "0.8.3"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk = "0.3"
|
||||
|
|
|
@ -190,7 +190,7 @@ fn render_info(simple_text: &mut SimpleText, sb: &mut SceneBuilder, info: &str)
|
|||
None,
|
||||
40.0,
|
||||
None,
|
||||
piet_scene::Affine::translate(110.0, 50.0),
|
||||
piet_scene::kurbo::Affine::translate((110.0, 50.0)),
|
||||
info,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ use std::str::FromStr;
|
|||
|
||||
use roxmltree::{Document, Node};
|
||||
|
||||
use kurbo::{Affine, BezPath};
|
||||
|
||||
use piet_scene::kurbo::{Affine, BezPath};
|
||||
use piet_scene::Color;
|
||||
|
||||
pub struct PicoSvg {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use piet_scene::{Color, GradientStop, GradientStops};
|
||||
use piet_scene::{Color, ColorStop, ColorStops};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -8,7 +8,7 @@ const RETAINED_COUNT: usize = 64;
|
|||
#[derive(Default)]
|
||||
pub struct RampCache {
|
||||
epoch: u64,
|
||||
map: HashMap<GradientStops, (u32, u64)>,
|
||||
map: HashMap<ColorStops, (u32, u64)>,
|
||||
data: Vec<u32>,
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ impl RampCache {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, stops: &[GradientStop]) -> u32 {
|
||||
pub fn add(&mut self, stops: &[ColorStop]) -> u32 {
|
||||
if let Some(entry) = self.map.get_mut(stops) {
|
||||
entry.1 = self.epoch;
|
||||
entry.0
|
||||
|
@ -64,7 +64,7 @@ impl RampCache {
|
|||
}
|
||||
}
|
||||
|
||||
fn make_ramp<'a>(stops: &'a [GradientStop]) -> impl Iterator<Item = u32> + 'a {
|
||||
fn make_ramp<'a>(stops: &'a [ColorStop]) -> impl Iterator<Item = u32> + 'a {
|
||||
let mut last_u = 0.0;
|
||||
let mut last_c = ColorF64::from_color(stops[0].color);
|
||||
let mut this_u = last_u;
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
use crate::PicoSvg;
|
||||
use kurbo::BezPath;
|
||||
use piet_scene::kurbo::{Affine, BezPath, Ellipse, PathEl, Point, Rect};
|
||||
use piet_scene::*;
|
||||
|
||||
use crate::SimpleText;
|
||||
|
||||
pub fn render_funky_paths(sb: &mut SceneBuilder) {
|
||||
use PathElement::*;
|
||||
use PathEl::*;
|
||||
let missing_movetos = [
|
||||
LineTo((100.0, 100.0).into()),
|
||||
LineTo((100.0, 200.0).into()),
|
||||
Close,
|
||||
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: [PathElement; 0] = [];
|
||||
let empty: [PathEl; 0] = [];
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::translate(100.0, 100.0),
|
||||
&Color::rgb8(0, 0, 255).into(),
|
||||
Affine::translate((100.0, 100.0)),
|
||||
Color::rgb8(0, 0, 255),
|
||||
None,
|
||||
missing_movetos,
|
||||
&missing_movetos,
|
||||
);
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&Color::rgb8(0, 0, 255).into(),
|
||||
Color::rgb8(0, 0, 255),
|
||||
None,
|
||||
empty,
|
||||
&empty,
|
||||
);
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&Color::rgb8(0, 0, 255).into(),
|
||||
Color::rgb8(0, 0, 255),
|
||||
None,
|
||||
only_movetos,
|
||||
&only_movetos,
|
||||
);
|
||||
sb.stroke(
|
||||
&simple_stroke(8.0),
|
||||
Affine::translate(100.0, 100.0),
|
||||
&Color::rgb8(0, 255, 255).into(),
|
||||
&Stroke::new(8.0),
|
||||
Affine::translate((100.0, 100.0)),
|
||||
Color::rgb8(0, 255, 255),
|
||||
None,
|
||||
missing_movetos,
|
||||
&missing_movetos,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -58,18 +58,18 @@ pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
|||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&fill.color.into(),
|
||||
fill.color,
|
||||
None,
|
||||
convert_bez_path(&fill.path),
|
||||
&fill.path,
|
||||
);
|
||||
}
|
||||
Item::Stroke(stroke) => {
|
||||
sb.stroke(
|
||||
&simple_stroke(stroke.width as f32),
|
||||
&Stroke::new(stroke.width as f32),
|
||||
Affine::IDENTITY,
|
||||
&stroke.color.into(),
|
||||
stroke.color,
|
||||
None,
|
||||
convert_bez_path(&stroke.path),
|
||||
&stroke.path,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -101,26 +101,26 @@ pub fn render_scene(sb: &mut SceneBuilder) {
|
|||
#[allow(unused)]
|
||||
fn render_cardioid(sb: &mut SceneBuilder) {
|
||||
let n = 601;
|
||||
let dth = std::f32::consts::PI * 2.0 / (n as f32);
|
||||
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 = vec![];
|
||||
let mut path = BezPath::new();
|
||||
for i in 1..n {
|
||||
let mut p0 = center;
|
||||
let a0 = i as f32 * dth;
|
||||
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 f32 * dth;
|
||||
let a1 = ((i * 2) % n) as f64 * dth;
|
||||
p1.x += a1.cos() * r;
|
||||
p1.y += a1.sin() * r;
|
||||
path.push(PathElement::MoveTo(p0));
|
||||
path.push(PathElement::LineTo(p1));
|
||||
path.push(PathEl::MoveTo(p0));
|
||||
path.push(PathEl::LineTo(p1));
|
||||
}
|
||||
sb.stroke(
|
||||
&simple_stroke(2.0),
|
||||
&Stroke::new(2.0),
|
||||
Affine::IDENTITY,
|
||||
&Brush::Solid(Color::rgb8(0, 0, 0)),
|
||||
Color::rgb8(0, 0, 0),
|
||||
None,
|
||||
&path,
|
||||
);
|
||||
|
@ -129,35 +129,32 @@ fn render_cardioid(sb: &mut SceneBuilder) {
|
|||
#[allow(unused)]
|
||||
fn render_clip_test(sb: &mut SceneBuilder) {
|
||||
const N: usize = 16;
|
||||
const X0: f32 = 50.0;
|
||||
const Y0: f32 = 450.0;
|
||||
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: f32 = 550.0;
|
||||
const Y1: f32 = 950.0;
|
||||
let step = 1.0 / ((N + 1) as f32);
|
||||
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 f32) * step;
|
||||
let path = &[
|
||||
PathElement::MoveTo((X0, Y0).into()),
|
||||
PathElement::LineTo((X1, Y0).into()),
|
||||
PathElement::LineTo((X1, Y0 + t * (Y1 - Y0)).into()),
|
||||
PathElement::LineTo((X1 + t * (X0 - X1), Y1).into()),
|
||||
PathElement::LineTo((X0, Y1).into()),
|
||||
PathElement::Close,
|
||||
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.into(), Affine::IDENTITY, path);
|
||||
sb.push_layer(Mix::Clip, Affine::IDENTITY, &path);
|
||||
}
|
||||
let rect = Rect {
|
||||
min: Point::new(X0, Y0),
|
||||
max: Point::new(X1, Y1),
|
||||
};
|
||||
let rect = Rect::new(X0, Y0, X1, Y1);
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&Brush::Solid(Color::rgb8(0, 0, 0)),
|
||||
None,
|
||||
rect.elements(),
|
||||
&rect,
|
||||
);
|
||||
for _ in 0..N {
|
||||
sb.pop_layer();
|
||||
|
@ -170,28 +167,24 @@ fn render_alpha_test(sb: &mut SceneBuilder) {
|
|||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&Color::rgb8(255, 0, 0).into(),
|
||||
Color::rgb8(255, 0, 0),
|
||||
None,
|
||||
make_diamond(1024.0, 100.0),
|
||||
&make_diamond(1024.0, 100.0),
|
||||
);
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&Color::rgba8(0, 255, 0, 0x80).into(),
|
||||
Color::rgba8(0, 255, 0, 0x80),
|
||||
None,
|
||||
make_diamond(1024.0, 125.0),
|
||||
);
|
||||
sb.push_layer(
|
||||
Mix::Clip.into(),
|
||||
Affine::IDENTITY,
|
||||
make_diamond(1024.0, 150.0),
|
||||
&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).into(),
|
||||
Color::rgba8(0, 0, 255, 0x80),
|
||||
None,
|
||||
make_diamond(1024.0, 175.0),
|
||||
&make_diamond(1024.0, 175.0),
|
||||
);
|
||||
sb.pop_layer();
|
||||
}
|
||||
|
@ -219,7 +212,7 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
|||
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 transform = Affine::translate((i as f64 * 225., j as f64 * 225.));
|
||||
let square = blend_square(blend.into());
|
||||
sb.append(&square, Some(transform));
|
||||
}
|
||||
|
@ -228,25 +221,10 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
|||
#[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)] = &[
|
||||
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)),
|
||||
|
@ -254,62 +232,30 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin
|
|||
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());
|
||||
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.into(), transform, rect.elements());
|
||||
sb.push_layer(Mix::Normal, transform, &rect);
|
||||
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());
|
||||
let linear = LinearGradient::new((0.0, 0.0), (0.0, 200.0)).stops([Color::WHITE, *c]);
|
||||
sb.push_layer(blend, transform, &rect);
|
||||
// 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.);
|
||||
* 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,
|
||||
make_ellipse(100., 100., 90., 90.),
|
||||
&Ellipse::new((100., 100.), (90., 90.), 0.),
|
||||
);
|
||||
sb.pop_layer();
|
||||
}
|
||||
|
@ -332,7 +278,7 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize)
|
|||
Affine::IDENTITY,
|
||||
&Brush::Solid(Color::rgb8(128, 128, 128)),
|
||||
None,
|
||||
Rect::from_origin_size(Point::new(0.0, 0.0), 1000.0, 1000.0).elements(),
|
||||
&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!";
|
||||
|
@ -341,7 +287,7 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize)
|
|||
None,
|
||||
text_size,
|
||||
None,
|
||||
Affine::translate(110.0, 600.0),
|
||||
Affine::translate((110.0, 600.0)),
|
||||
s,
|
||||
);
|
||||
text.add(
|
||||
|
@ -349,121 +295,58 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize)
|
|||
None,
|
||||
text_size,
|
||||
None,
|
||||
Affine::translate(110.0, 700.0),
|
||||
Affine::translate((110.0, 700.0)),
|
||||
s,
|
||||
);
|
||||
let th = (std::f32::consts::PI / 180.0) * (i as f32);
|
||||
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(
|
||||
&simple_stroke(5.0),
|
||||
&Stroke::new(5.0),
|
||||
Affine::IDENTITY,
|
||||
&Brush::Solid(Color::rgb8(128, 0, 0)),
|
||||
None,
|
||||
&[PathElement::MoveTo(center), PathElement::LineTo(p1)],
|
||||
&&[PathEl::MoveTo(center), PathEl::LineTo(p1)][..],
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn render_brush_transform(sb: &mut SceneBuilder, i: usize) {
|
||||
let th = (std::f32::consts::PI / 180.0) * (i as f32);
|
||||
let stops = &[
|
||||
GradientStop {
|
||||
color: Color::rgb8(255, 0, 0),
|
||||
offset: 0.0,
|
||||
},
|
||||
GradientStop {
|
||||
color: Color::rgb8(0, 255, 0),
|
||||
offset: 0.5,
|
||||
},
|
||||
GradientStop {
|
||||
color: Color::rgb8(0, 0, 255),
|
||||
offset: 1.0,
|
||||
},
|
||||
][..];
|
||||
let linear = LinearGradient {
|
||||
start: Point::new(0.0, 0.0),
|
||||
end: Point::new(0.0, 200.0),
|
||||
stops: stops.into(),
|
||||
extend: ExtendMode::Pad,
|
||||
}
|
||||
.into();
|
||||
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),
|
||||
Affine::translate((200.0, 200.0)),
|
||||
&linear,
|
||||
Some(Affine::rotate(th).around_center(200.0, 100.0)),
|
||||
Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(),
|
||||
Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))),
|
||||
&Rect::from_origin_size(Point::default(), (400.0, 200.0)),
|
||||
);
|
||||
sb.stroke(
|
||||
&simple_stroke(40.0),
|
||||
Affine::translate(800.0, 200.0),
|
||||
&Stroke::new(40.0),
|
||||
Affine::translate((800.0, 200.0)),
|
||||
&linear,
|
||||
Some(Affine::rotate(th).around_center(200.0, 100.0)),
|
||||
Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(),
|
||||
Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))),
|
||||
&Rect::from_origin_size(Point::default(), (400.0, 200.0)),
|
||||
);
|
||||
}
|
||||
|
||||
fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator<Item = PathElement> + 'a + Clone {
|
||||
path.elements()
|
||||
.iter()
|
||||
.map(|el| PathElement::from_kurbo(*el))
|
||||
fn around_center(xform: Affine, center: Point) -> Affine {
|
||||
Affine::translate(center.to_vec2()) * xform * Affine::translate(-center.to_vec2())
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
fn make_diamond(cx: f32, cy: f32) -> impl Iterator<Item = PathElement> + Clone {
|
||||
const SIZE: f32 = 50.0;
|
||||
let elements = [
|
||||
PathElement::MoveTo(Point::new(cx, cy - SIZE)),
|
||||
PathElement::LineTo(Point::new(cx + SIZE, cy)),
|
||||
PathElement::LineTo(Point::new(cx, cy + SIZE)),
|
||||
PathElement::LineTo(Point::new(cx - SIZE, cy)),
|
||||
PathElement::Close,
|
||||
];
|
||||
(0..elements.len()).map(move |i| elements[i])
|
||||
}
|
||||
|
||||
fn simple_stroke(width: f32) -> Stroke<[f32; 0]> {
|
||||
Stroke {
|
||||
width,
|
||||
join: Join::Round,
|
||||
miter_limit: 1.4,
|
||||
start_cap: Cap::Round,
|
||||
end_cap: Cap::Round,
|
||||
dash_pattern: [],
|
||||
dash_offset: 0.0,
|
||||
scale: true,
|
||||
}
|
||||
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,
|
||||
]
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
use piet_scene::glyph::{pinot, pinot::TableProvider, GlyphContext};
|
||||
use piet_scene::{Affine, Brush, SceneBuilder};
|
||||
use piet_scene::kurbo::Affine;
|
||||
use piet_scene::{Brush, SceneBuilder};
|
||||
|
||||
pub use pinot::FontRef;
|
||||
|
||||
|
@ -49,8 +50,8 @@ impl SimpleText {
|
|||
});
|
||||
if let Some(cmap) = font.cmap() {
|
||||
if let Some(hmtx) = font.hmtx() {
|
||||
let upem = font.head().map(|head| head.units_per_em()).unwrap_or(1000) as f32;
|
||||
let scale = size / upem;
|
||||
let upem = font.head().map(|head| head.units_per_em()).unwrap_or(1000) as f64;
|
||||
let scale = size as f64 / upem;
|
||||
let vars: [(pinot::types::Tag, f32); 0] = [];
|
||||
let mut provider = self.gcx.new_provider(font, None, size, false, vars);
|
||||
let hmetrics = hmtx.hmetrics();
|
||||
|
@ -58,17 +59,18 @@ impl SimpleText {
|
|||
.get(hmetrics.len().saturating_sub(1))
|
||||
.map(|h| h.advance_width)
|
||||
.unwrap_or(0);
|
||||
let mut pen_x = 0f32;
|
||||
let mut pen_x = 0f64;
|
||||
for ch in text.chars() {
|
||||
let gid = cmap.map(ch as u32).unwrap_or(0);
|
||||
let advance = hmetrics
|
||||
.get(gid as usize)
|
||||
.map(|h| h.advance_width)
|
||||
.unwrap_or(default_advance) as f32
|
||||
.unwrap_or(default_advance) as f64
|
||||
* scale;
|
||||
if let Some(glyph) = provider.get(gid, brush) {
|
||||
let xform =
|
||||
transform * Affine::translate(pen_x, 0.0) * Affine::scale(1.0, -1.0);
|
||||
let xform = transform
|
||||
* Affine::translate((pen_x, 0.0))
|
||||
* Affine::scale_non_uniform(1.0, -1.0);
|
||||
builder.append(&glyph, Some(xform));
|
||||
}
|
||||
pen_x += advance;
|
||||
|
|
|
@ -8,4 +8,4 @@ edition = "2021"
|
|||
bytemuck = { version = "1.7.2", features = ["derive"] }
|
||||
smallvec = "1.8.0"
|
||||
moscato = { git = "https://github.com/dfrg/pinot" }
|
||||
kurbo = { version = "0.8.3", optional = true }
|
||||
peniko = { git = "https://github.com/linebender/peniko" }
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// 32-bit RGBA color.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub struct Color {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const fn rgb8(r: u8, g: u8, b: u8) -> Self {
|
||||
Self { r, g, b, a: 255 }
|
||||
}
|
||||
|
||||
pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
|
||||
pub fn to_premul_u32(self) -> u32 {
|
||||
let a = self.a as f64 * (1.0 / 255.0);
|
||||
let r = (self.r as f64 * a).round() as u32;
|
||||
let g = (self.g as f64 * a).round() as u32;
|
||||
let b = (self.b as f64 * a).round() as u32;
|
||||
(r << 24) | (g << 16) | (b << 8) | self.a as u32
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::color::Color;
|
||||
use super::ExtendMode;
|
||||
use crate::geometry::Point;
|
||||
use smallvec::SmallVec;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// Offset and color of a transition point in a gradient.
|
||||
#[derive(Copy, Clone, PartialOrd, Default, Debug)]
|
||||
pub struct GradientStop {
|
||||
pub offset: f32,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
impl Hash for GradientStop {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.offset.to_bits().hash(state);
|
||||
self.color.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Override PartialEq to use to_bits for the offset to match with the Hash impl
|
||||
impl std::cmp::PartialEq for GradientStop {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.offset.to_bits() == other.offset.to_bits() && self.color == other.color
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::Eq for GradientStop {}
|
||||
|
||||
/// Collection of gradient stops.
|
||||
pub type GradientStops = SmallVec<[GradientStop; 4]>;
|
||||
|
||||
/// Definition of a gradient that transitions between two or more colors along
|
||||
/// a line.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LinearGradient {
|
||||
pub start: Point,
|
||||
pub end: Point,
|
||||
pub stops: GradientStops,
|
||||
pub extend: ExtendMode,
|
||||
}
|
||||
|
||||
/// Definition of a gradient that transitions between two or more colors that
|
||||
/// radiate from an origin.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RadialGradient {
|
||||
pub center0: Point,
|
||||
pub radius0: f32,
|
||||
pub center1: Point,
|
||||
pub radius1: f32,
|
||||
pub stops: GradientStops,
|
||||
pub extend: ExtendMode,
|
||||
}
|
||||
|
||||
/// Definition gradient that transitions between two or more colors that rotate
|
||||
/// around a center point.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SweepGradient {
|
||||
pub center: Point,
|
||||
pub start_angle: f32,
|
||||
pub end_angle: f32,
|
||||
pub stops: GradientStops,
|
||||
pub extend: ExtendMode,
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use std::result::Result;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Image data resource.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Image(Arc<Inner>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Inner {
|
||||
id: u64,
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: Arc<[u8]>,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub fn new(
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: impl Into<Arc<[u8]>>,
|
||||
) -> Result<Self, ImageDataSizeError> {
|
||||
let data_size = width
|
||||
.checked_mul(height)
|
||||
.and_then(|x| x.checked_mul(4))
|
||||
.ok_or(ImageDataSizeError)? as usize;
|
||||
let data = data.into();
|
||||
if data.len() < data_size {
|
||||
return Err(ImageDataSizeError);
|
||||
}
|
||||
static ID: AtomicU64 = AtomicU64::new(1);
|
||||
Ok(Self(Arc::new(Inner {
|
||||
id: ID.fetch_add(1, Ordering::Relaxed),
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u64 {
|
||||
self.0.id
|
||||
}
|
||||
|
||||
pub fn width(&self) -> u32 {
|
||||
self.0.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
self.0.height
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
&self.0.data
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned when image data size is not sufficient for the specified
|
||||
/// dimensions.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImageDataSizeError;
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
mod color;
|
||||
mod gradient;
|
||||
mod image;
|
||||
|
||||
pub use color::Color;
|
||||
pub use gradient::*;
|
||||
pub use image::*;
|
||||
|
||||
/// Describes the content of a filled or stroked shape.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Brush {
|
||||
Solid(Color),
|
||||
LinearGradient(LinearGradient),
|
||||
RadialGradient(RadialGradient),
|
||||
SweepGradient(SweepGradient),
|
||||
Image(Image),
|
||||
}
|
||||
|
||||
/// Defines how a brush is extended when the content does not
|
||||
/// completely fill a shape.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum ExtendMode {
|
||||
Pad,
|
||||
Repeat,
|
||||
Reflect,
|
||||
}
|
||||
|
||||
impl From<Color> for Brush {
|
||||
fn from(c: Color) -> Self {
|
||||
Self::Solid(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LinearGradient> for Brush {
|
||||
fn from(g: LinearGradient) -> Self {
|
||||
Self::LinearGradient(g)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RadialGradient> for Brush {
|
||||
fn from(g: RadialGradient) -> Self {
|
||||
Self::RadialGradient(g)
|
||||
}
|
||||
}
|
28
piet-scene/src/conv.rs
Normal file
28
piet-scene/src/conv.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use peniko::kurbo::{Affine, Point};
|
||||
|
||||
pub fn affine_to_f32(affine: &Affine) -> [f32; 6] {
|
||||
let c = affine.as_coeffs();
|
||||
[
|
||||
c[0] as f32,
|
||||
c[1] as f32,
|
||||
c[2] as f32,
|
||||
c[3] as f32,
|
||||
c[4] as f32,
|
||||
c[5] as f32,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn affine_from_f32(coeffs: &[f32; 6]) -> Affine {
|
||||
Affine::new([
|
||||
coeffs[0] as f64,
|
||||
coeffs[1] as f64,
|
||||
coeffs[2] as f64,
|
||||
coeffs[3] as f64,
|
||||
coeffs[4] as f64,
|
||||
coeffs[5] as f64,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn point_to_f32(point: Point) -> [f32; 2] {
|
||||
[point.x as f32, point.y as f32]
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// This module is based in part on kurbo (https://github.com/linebender/kurbo)
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use core::borrow::Borrow;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
/// Two dimensional point.
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Point {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Hash for Point {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.x.to_bits().hash(state);
|
||||
self.y.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub const fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn transform(&self, affine: &Affine) -> Self {
|
||||
Self {
|
||||
x: self.x * affine.xx + self.y * affine.yx + affine.dx,
|
||||
y: self.y * affine.yy + self.y * affine.xy + affine.dy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 2]> for Point {
|
||||
fn from(value: [f32; 2]) -> Self {
|
||||
Self::new(value[0], value[1])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32)> for Point {
|
||||
fn from(value: (f32, f32)) -> Self {
|
||||
Self::new(value.0, value.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Affine transformation matrix.
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Affine {
|
||||
pub xx: f32,
|
||||
pub yx: f32,
|
||||
pub xy: f32,
|
||||
pub yy: f32,
|
||||
pub dx: f32,
|
||||
pub dy: f32,
|
||||
}
|
||||
|
||||
impl Affine {
|
||||
pub const IDENTITY: Self = Self {
|
||||
xx: 1.0,
|
||||
yx: 0.0,
|
||||
xy: 0.0,
|
||||
yy: 1.0,
|
||||
dx: 0.0,
|
||||
dy: 0.0,
|
||||
};
|
||||
|
||||
pub const fn new(elements: &[f32; 6]) -> Self {
|
||||
Self {
|
||||
xx: elements[0],
|
||||
yx: elements[1],
|
||||
xy: elements[2],
|
||||
yy: elements[3],
|
||||
dx: elements[4],
|
||||
dy: elements[5],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new affine transform representing the specified scale along the
|
||||
/// x and y axes.
|
||||
pub fn scale(x: f32, y: f32) -> Self {
|
||||
Self::new(&[x, 0., 0., y, 0., 0.])
|
||||
}
|
||||
|
||||
/// Creates a new affine transform representing the specified translation.
|
||||
pub fn translate(x: f32, y: f32) -> Self {
|
||||
Self::new(&[1., 0., 0., 1., x, y])
|
||||
}
|
||||
|
||||
/// Creates a new affine transform representing a counter-clockwise
|
||||
/// rotation for the specified angle in radians.
|
||||
pub fn rotate(th: f32) -> Self {
|
||||
let (s, c) = th.sin_cos();
|
||||
Self::new(&[c, s, -s, c, 0., 0.])
|
||||
}
|
||||
|
||||
/// Creates a new skew transform
|
||||
pub fn skew(x: f32, y: f32) -> Self {
|
||||
Self::new(&[1., x.tan(), y.tan(), 1., 0., 0.])
|
||||
}
|
||||
|
||||
pub fn around_center(&self, x: f32, y: f32) -> Self {
|
||||
Self::translate(x, y) * *self * Self::translate(-x, -y)
|
||||
}
|
||||
|
||||
/// Transforms the specified point.
|
||||
pub fn transform_point(&self, point: Point) -> Point {
|
||||
Point {
|
||||
x: point.x * self.xx + point.y * self.yx + self.dx,
|
||||
y: point.y * self.yy + point.y * self.xy + self.dy,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the determinant of this transform.
|
||||
pub fn determinant(self) -> f32 {
|
||||
self.xx * self.yy - self.yx * self.xy
|
||||
}
|
||||
|
||||
/// Compute the inverse transform.
|
||||
///
|
||||
/// Produces NaN values when the determinant is zero.
|
||||
pub fn inverse(self) -> Self {
|
||||
let inv_det = self.determinant().recip();
|
||||
Self::new(&[
|
||||
inv_det * self.yy,
|
||||
-inv_det * self.yx,
|
||||
-inv_det * self.xy,
|
||||
inv_det * self.xx,
|
||||
inv_det * (self.xy * self.dy - self.yy * self.dx),
|
||||
inv_det * (self.yx * self.dx - self.xx * self.dy),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Affine {
|
||||
fn default() -> Self {
|
||||
Self::IDENTITY
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for Affine {
|
||||
type Output = Self;
|
||||
fn mul(self, other: Self) -> Self {
|
||||
Self::new(&[
|
||||
self.xx * other.xx + self.xy * other.yx,
|
||||
self.yx * other.xx + self.yy * other.yx,
|
||||
self.xx * other.xy + self.xy * other.yy,
|
||||
self.yx * other.xy + self.yy * other.yy,
|
||||
self.xx * other.dx + self.xy * other.dy + self.dx,
|
||||
self.yx * other.dx + self.yy * other.dy + self.dy,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// Axis-aligned rectangle represented as minimum and maximum points.
|
||||
#[derive(Copy, Clone, Default, Debug, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Rect {
|
||||
pub min: Point,
|
||||
pub max: Point,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
/// Creates a new rectangle that encloses the specified collection of
|
||||
/// points.
|
||||
pub fn from_points<I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Point>,
|
||||
{
|
||||
let mut rect = Self {
|
||||
min: Point::new(f32::MAX, f32::MAX),
|
||||
max: Point::new(f32::MIN, f32::MIN),
|
||||
};
|
||||
let mut count = 0;
|
||||
for point in points {
|
||||
rect.add(*point.borrow());
|
||||
count += 1;
|
||||
}
|
||||
if count != 0 {
|
||||
rect
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new rectangle from an origin point and dimensions.
|
||||
pub fn from_origin_size(origin: Point, width: f32, height: f32) -> Self {
|
||||
Self {
|
||||
min: origin,
|
||||
max: Point::new(origin.x + width, origin.y + height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the width of the rectangle.
|
||||
pub fn width(&self) -> f32 {
|
||||
self.max.x - self.min.x
|
||||
}
|
||||
|
||||
/// Returns the height of the rectangle.
|
||||
pub fn height(&self) -> f32 {
|
||||
self.max.y - self.min.y
|
||||
}
|
||||
|
||||
/// Extends the rectangle to include the specified point.
|
||||
pub fn add(&mut self, point: Point) {
|
||||
self.min.x = self.min.x.min(point.x);
|
||||
self.min.y = self.min.y.min(point.y);
|
||||
self.max.x = self.max.x.max(point.x);
|
||||
self.max.y = self.max.y.max(point.y);
|
||||
}
|
||||
|
||||
/// Returns a new rectangle that encloses the minimum and maximum points
|
||||
/// of this rectangle after applying the specified transform to each.
|
||||
pub fn transform(&self, affine: &Affine) -> Self {
|
||||
Self::from_points([self.min.transform(affine), self.max.transform(affine)])
|
||||
}
|
||||
}
|
|
@ -18,10 +18,9 @@
|
|||
|
||||
pub use moscato::pinot;
|
||||
|
||||
use crate::brush::{Brush, Color};
|
||||
use crate::geometry::Affine;
|
||||
use crate::path::PathElement;
|
||||
use crate::scene::{Fill, SceneBuilder, SceneFragment};
|
||||
use crate::scene::{SceneBuilder, SceneFragment};
|
||||
use peniko::kurbo::{Affine, Rect};
|
||||
use peniko::{Brush, Color, Fill, Mix};
|
||||
|
||||
use moscato::{Context, Scaler};
|
||||
use pinot::{types::Tag, FontRef};
|
||||
|
@ -93,7 +92,7 @@ impl<'a> GlyphProvider<'a> {
|
|||
Affine::IDENTITY,
|
||||
brush.unwrap_or(&Brush::Solid(Color::rgb8(255, 255, 255))),
|
||||
None,
|
||||
convert_path(path.elements()),
|
||||
&convert_path(path.elements()),
|
||||
);
|
||||
builder.finish();
|
||||
Some(fragment)
|
||||
|
@ -102,7 +101,6 @@ impl<'a> GlyphProvider<'a> {
|
|||
/// Returns a scene fragment containing the commands and resources to
|
||||
/// render the specified color glyph.
|
||||
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<SceneFragment> {
|
||||
use crate::geometry::*;
|
||||
use moscato::Command;
|
||||
let glyph = self.scaler.color_glyph(palette_index, gid)?;
|
||||
let mut fragment = SceneFragment::default();
|
||||
|
@ -125,39 +123,39 @@ impl<'a> GlyphProvider<'a> {
|
|||
let path = glyph.path(*path_index)?;
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
builder.push_layer(
|
||||
Default::default(),
|
||||
Mix::Clip,
|
||||
Affine::IDENTITY,
|
||||
convert_transformed_path(path.elements(), xform),
|
||||
&convert_transformed_path(path.elements(), xform),
|
||||
);
|
||||
} else {
|
||||
builder.push_layer(
|
||||
Default::default(),
|
||||
Mix::Clip,
|
||||
Affine::IDENTITY,
|
||||
convert_path(path.elements()),
|
||||
&convert_path(path.elements()),
|
||||
);
|
||||
}
|
||||
}
|
||||
Command::PopClip => builder.pop_layer(),
|
||||
Command::PushLayer(bounds) => {
|
||||
let mut rect = Rect {
|
||||
min: Point::new(bounds.min.x, bounds.min.y),
|
||||
max: Point::new(bounds.max.x, bounds.max.y),
|
||||
};
|
||||
let mut min = convert_point(bounds.min);
|
||||
let mut max = convert_point(bounds.max);
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
rect = rect.transform(xform);
|
||||
min = *xform * min;
|
||||
max = *xform * max;
|
||||
}
|
||||
builder.push_layer(Default::default(), Affine::IDENTITY, rect.elements());
|
||||
let rect = Rect::from_points(min, max);
|
||||
builder.push_layer(Mix::Normal, Affine::IDENTITY, &rect);
|
||||
}
|
||||
Command::PopLayer => builder.pop_layer(),
|
||||
Command::BeginBlend(bounds, mode) => {
|
||||
let mut rect = Rect {
|
||||
min: Point::new(bounds.min.x, bounds.min.y),
|
||||
max: Point::new(bounds.max.x, bounds.max.y),
|
||||
};
|
||||
let mut min = convert_point(bounds.min);
|
||||
let mut max = convert_point(bounds.max);
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
rect = rect.transform(xform);
|
||||
min = *xform * min;
|
||||
max = *xform * max;
|
||||
}
|
||||
builder.push_layer(convert_blend(*mode), Affine::IDENTITY, rect.elements())
|
||||
let rect = Rect::from_points(min, max);
|
||||
builder.push_layer(convert_blend(*mode), Affine::IDENTITY, &rect);
|
||||
}
|
||||
Command::EndBlend => builder.pop_layer(),
|
||||
Command::SimpleFill(path_index, brush, brush_xform) => {
|
||||
|
@ -170,7 +168,7 @@ impl<'a> GlyphProvider<'a> {
|
|||
Affine::IDENTITY,
|
||||
&brush,
|
||||
brush_xform.map(|x| x * *xform),
|
||||
convert_transformed_path(path.elements(), xform),
|
||||
&convert_transformed_path(path.elements(), xform),
|
||||
);
|
||||
} else {
|
||||
builder.fill(
|
||||
|
@ -178,7 +176,7 @@ impl<'a> GlyphProvider<'a> {
|
|||
Affine::IDENTITY,
|
||||
&brush,
|
||||
brush_xform,
|
||||
convert_path(path.elements()),
|
||||
&convert_path(path.elements()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -193,54 +191,28 @@ impl<'a> GlyphProvider<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_path(
|
||||
path: impl Iterator<Item = moscato::Element> + Clone,
|
||||
) -> impl Iterator<Item = PathElement> + Clone {
|
||||
use crate::geometry::Point;
|
||||
path.map(|el| match el {
|
||||
moscato::Element::MoveTo(p0) => PathElement::MoveTo(Point::new(p0.x, p0.y)),
|
||||
moscato::Element::LineTo(p0) => PathElement::LineTo(Point::new(p0.x, p0.y)),
|
||||
moscato::Element::QuadTo(p0, p1) => {
|
||||
PathElement::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y))
|
||||
}
|
||||
moscato::Element::CurveTo(p0, p1, p2) => PathElement::CurveTo(
|
||||
Point::new(p0.x, p0.y),
|
||||
Point::new(p1.x, p1.y),
|
||||
Point::new(p2.x, p2.y),
|
||||
),
|
||||
moscato::Element::Close => PathElement::Close,
|
||||
})
|
||||
fn convert_path(path: impl Iterator<Item = moscato::Element> + Clone) -> peniko::kurbo::BezPath {
|
||||
let mut result = peniko::kurbo::BezPath::new();
|
||||
for el in path {
|
||||
result.push(convert_path_el(&el));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn convert_transformed_path(
|
||||
path: impl Iterator<Item = moscato::Element> + Clone,
|
||||
xform: &Affine,
|
||||
) -> impl Iterator<Item = PathElement> + Clone {
|
||||
use crate::geometry::Point;
|
||||
let xform = *xform;
|
||||
path.map(move |el| match el {
|
||||
moscato::Element::MoveTo(p0) => {
|
||||
PathElement::MoveTo(Point::new(p0.x, p0.y).transform(&xform))
|
||||
}
|
||||
moscato::Element::LineTo(p0) => {
|
||||
PathElement::LineTo(Point::new(p0.x, p0.y).transform(&xform))
|
||||
}
|
||||
moscato::Element::QuadTo(p0, p1) => PathElement::QuadTo(
|
||||
Point::new(p0.x, p0.y).transform(&xform),
|
||||
Point::new(p1.x, p1.y).transform(&xform),
|
||||
),
|
||||
moscato::Element::CurveTo(p0, p1, p2) => PathElement::CurveTo(
|
||||
Point::new(p0.x, p0.y).transform(&xform),
|
||||
Point::new(p1.x, p1.y).transform(&xform),
|
||||
Point::new(p2.x, p2.y).transform(&xform),
|
||||
),
|
||||
moscato::Element::Close => PathElement::Close,
|
||||
})
|
||||
) -> peniko::kurbo::BezPath {
|
||||
let mut result = peniko::kurbo::BezPath::new();
|
||||
for el in path {
|
||||
result.push(*xform * convert_path_el(&el));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::BlendMode {
|
||||
use crate::scene::{BlendMode, Compose, Mix};
|
||||
fn convert_blend(mode: moscato::CompositeMode) -> peniko::BlendMode {
|
||||
use moscato::CompositeMode;
|
||||
use peniko::{BlendMode, Compose};
|
||||
let mut mix = Mix::Normal;
|
||||
let mut compose = Compose::SrcOver;
|
||||
match mode {
|
||||
|
@ -276,20 +248,23 @@ fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::BlendMode {
|
|||
BlendMode { mix, compose }
|
||||
}
|
||||
|
||||
fn convert_transform(xform: &moscato::Transform) -> crate::geometry::Affine {
|
||||
crate::geometry::Affine {
|
||||
xx: xform.xx,
|
||||
yx: xform.yx,
|
||||
xy: xform.xy,
|
||||
yy: xform.yy,
|
||||
dx: xform.dx,
|
||||
dy: xform.dy,
|
||||
}
|
||||
fn convert_transform(xform: &moscato::Transform) -> peniko::kurbo::Affine {
|
||||
peniko::kurbo::Affine::new([
|
||||
xform.xx as f64,
|
||||
xform.yx as f64,
|
||||
xform.xy as f64,
|
||||
xform.yy as f64,
|
||||
xform.dx as f64,
|
||||
xform.dy as f64,
|
||||
])
|
||||
}
|
||||
|
||||
fn convert_brush(brush: &moscato::Brush) -> crate::brush::Brush {
|
||||
use crate::brush::*;
|
||||
use crate::geometry::*;
|
||||
fn convert_point(point: moscato::Point) -> peniko::kurbo::Point {
|
||||
peniko::kurbo::Point::new(point.x as f64, point.y as f64)
|
||||
}
|
||||
|
||||
fn convert_brush(brush: &moscato::Brush) -> peniko::Brush {
|
||||
use peniko::{LinearGradient, RadialGradient};
|
||||
match brush {
|
||||
moscato::Brush::Solid(color) => Brush::Solid(Color {
|
||||
r: color.r,
|
||||
|
@ -298,43 +273,58 @@ fn convert_brush(brush: &moscato::Brush) -> crate::brush::Brush {
|
|||
a: color.a,
|
||||
}),
|
||||
moscato::Brush::LinearGradient(grad) => Brush::LinearGradient(LinearGradient {
|
||||
start: Point::new(grad.start.x, grad.start.y),
|
||||
end: Point::new(grad.end.x, grad.end.y),
|
||||
start: convert_point(grad.start),
|
||||
end: convert_point(grad.end),
|
||||
stops: convert_stops(&grad.stops),
|
||||
extend: convert_extend(grad.extend),
|
||||
}),
|
||||
moscato::Brush::RadialGradient(grad) => Brush::RadialGradient(RadialGradient {
|
||||
center0: Point::new(grad.center0.x, grad.center0.y),
|
||||
center1: Point::new(grad.center1.x, grad.center1.y),
|
||||
radius0: grad.radius0,
|
||||
radius1: grad.radius1,
|
||||
start_center: convert_point(grad.center0),
|
||||
end_center: convert_point(grad.center1),
|
||||
start_radius: grad.radius0,
|
||||
end_radius: grad.radius1,
|
||||
stops: convert_stops(&grad.stops),
|
||||
extend: convert_extend(grad.extend),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::GradientStops {
|
||||
use crate::brush::GradientStop;
|
||||
fn convert_stops(stops: &[moscato::ColorStop]) -> peniko::ColorStops {
|
||||
stops
|
||||
.iter()
|
||||
.map(|stop| GradientStop {
|
||||
offset: stop.offset,
|
||||
color: Color {
|
||||
r: stop.color.r,
|
||||
g: stop.color.g,
|
||||
b: stop.color.b,
|
||||
a: stop.color.a,
|
||||
},
|
||||
.map(|stop| {
|
||||
(
|
||||
stop.offset,
|
||||
Color {
|
||||
r: stop.color.r,
|
||||
g: stop.color.g,
|
||||
b: stop.color.b,
|
||||
a: stop.color.a,
|
||||
},
|
||||
)
|
||||
.into()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::ExtendMode {
|
||||
use crate::brush::ExtendMode::*;
|
||||
fn convert_extend(extend: moscato::ExtendMode) -> peniko::Extend {
|
||||
use peniko::Extend::*;
|
||||
match extend {
|
||||
moscato::ExtendMode::Pad => Pad,
|
||||
moscato::ExtendMode::Repeat => Repeat,
|
||||
moscato::ExtendMode::Reflect => Reflect,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_path_el(el: &moscato::Element) -> peniko::kurbo::PathEl {
|
||||
use peniko::kurbo::PathEl::*;
|
||||
match el {
|
||||
moscato::Element::MoveTo(p0) => MoveTo(convert_point(*p0)),
|
||||
moscato::Element::LineTo(p0) => LineTo(convert_point(*p0)),
|
||||
moscato::Element::QuadTo(p0, p1) => QuadTo(convert_point(*p0), convert_point(*p1)),
|
||||
moscato::Element::CurveTo(p0, p1, p2) => {
|
||||
CurveTo(convert_point(*p0), convert_point(*p1), convert_point(*p2))
|
||||
}
|
||||
moscato::Element::Close => ClosePath,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,117 +14,12 @@
|
|||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
mod brush;
|
||||
mod geometry;
|
||||
mod path;
|
||||
mod resource;
|
||||
pub use peniko;
|
||||
|
||||
mod conv;
|
||||
mod scene;
|
||||
|
||||
pub mod glyph;
|
||||
|
||||
pub use brush::*;
|
||||
pub use geometry::*;
|
||||
pub use path::*;
|
||||
pub use resource::*;
|
||||
pub use peniko::*;
|
||||
pub use scene::*;
|
||||
|
||||
/// Implement conversions to and from Kurbo types when the `kurbo` feature is
|
||||
/// enabled.
|
||||
#[cfg(feature = "kurbo")]
|
||||
mod kurbo_conv {
|
||||
use super::geometry::{Affine, Point, Rect};
|
||||
use super::path::PathElement;
|
||||
|
||||
impl Point {
|
||||
/// Creates a new point from the equivalent kurbo type.
|
||||
pub fn from_kurbo(point: kurbo::Point) -> Self {
|
||||
Self::new(point.x as f32, point.y as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point> for kurbo::Point {
|
||||
fn from(p: Point) -> kurbo::Point {
|
||||
Self::new(p.x as f64, p.y as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Affine {
|
||||
/// Creates a new affine transformation from the equivalent kurbo type.
|
||||
pub fn from_kurbo(affine: kurbo::Affine) -> Self {
|
||||
let c = affine.as_coeffs();
|
||||
Self {
|
||||
xx: c[0] as f32,
|
||||
yx: c[1] as f32,
|
||||
xy: c[2] as f32,
|
||||
yy: c[3] as f32,
|
||||
dx: c[4] as f32,
|
||||
dy: c[5] as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Affine> for kurbo::Affine {
|
||||
fn from(a: Affine) -> Self {
|
||||
Self::new([
|
||||
a.xx as f64,
|
||||
a.yx as f64,
|
||||
a.yx as f64,
|
||||
a.yy as f64,
|
||||
a.dx as f64,
|
||||
a.dy as f64,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
/// Creates a new rectangle from the equivalent kurbo type.
|
||||
pub fn from_kurbo(rect: kurbo::Rect) -> Self {
|
||||
Self {
|
||||
min: Point::new(rect.x0 as f32, rect.y0 as f32),
|
||||
max: Point::new(rect.x1 as f32, rect.y1 as f32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rect> for kurbo::Rect {
|
||||
fn from(r: Rect) -> Self {
|
||||
Self {
|
||||
x0: r.min.x as f64,
|
||||
y0: r.min.y as f64,
|
||||
x1: r.max.x as f64,
|
||||
y1: r.max.y as f64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PathElement {
|
||||
/// Creates a new path element from the equivalent kurbo type.
|
||||
pub fn from_kurbo(el: kurbo::PathEl) -> Self {
|
||||
use kurbo::PathEl::*;
|
||||
match el {
|
||||
MoveTo(p0) => Self::MoveTo(Point::from_kurbo(p0)),
|
||||
LineTo(p0) => Self::LineTo(Point::from_kurbo(p0)),
|
||||
QuadTo(p0, p1) => Self::QuadTo(Point::from_kurbo(p0), Point::from_kurbo(p1)),
|
||||
CurveTo(p0, p1, p2) => Self::CurveTo(
|
||||
Point::from_kurbo(p0),
|
||||
Point::from_kurbo(p1),
|
||||
Point::from_kurbo(p2),
|
||||
),
|
||||
ClosePath => Self::Close,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathElement> for kurbo::PathEl {
|
||||
fn from(e: PathElement) -> Self {
|
||||
use PathElement::*;
|
||||
match e {
|
||||
MoveTo(p0) => Self::MoveTo(p0.into()),
|
||||
LineTo(p0) => Self::LineTo(p0.into()),
|
||||
QuadTo(p0, p1) => Self::QuadTo(p0.into(), p1.into()),
|
||||
CurveTo(p0, p1, p2) => Self::CurveTo(p0.into(), p1.into(), p2.into()),
|
||||
Close => Self::ClosePath,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::geometry::{Point, Rect};
|
||||
|
||||
/// Action of a path element.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum PathVerb {
|
||||
MoveTo,
|
||||
LineTo,
|
||||
QuadTo,
|
||||
CurveTo,
|
||||
Close,
|
||||
}
|
||||
|
||||
/// Element of a path represented by a verb and its associated points.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum PathElement {
|
||||
MoveTo(Point),
|
||||
LineTo(Point),
|
||||
QuadTo(Point, Point),
|
||||
CurveTo(Point, Point, Point),
|
||||
Close,
|
||||
}
|
||||
|
||||
impl PathElement {
|
||||
/// Returns the verb that describes the action of the path element.
|
||||
pub fn verb(&self) -> PathVerb {
|
||||
match self {
|
||||
Self::MoveTo(..) => PathVerb::MoveTo,
|
||||
Self::LineTo(..) => PathVerb::LineTo,
|
||||
Self::QuadTo(..) => PathVerb::QuadTo,
|
||||
Self::CurveTo(..) => PathVerb::CurveTo,
|
||||
Self::Close => PathVerb::Close,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn elements(&self) -> impl Iterator<Item = PathElement> + Clone {
|
||||
let elements = [
|
||||
PathElement::MoveTo((self.min.x, self.min.y).into()),
|
||||
PathElement::LineTo((self.max.x, self.min.y).into()),
|
||||
PathElement::LineTo((self.max.x, self.max.y).into()),
|
||||
PathElement::LineTo((self.min.x, self.max.y).into()),
|
||||
PathElement::Close,
|
||||
];
|
||||
(0..5).map(move |i| elements[i])
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// Defines the color mixing function for a blend operation.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum Mix {
|
||||
Normal = 0,
|
||||
Multiply = 1,
|
||||
Screen = 2,
|
||||
Overlay = 3,
|
||||
Darken = 4,
|
||||
Lighten = 5,
|
||||
ColorDodge = 6,
|
||||
ColorBurn = 7,
|
||||
HardLight = 8,
|
||||
SoftLight = 9,
|
||||
Difference = 10,
|
||||
Exclusion = 11,
|
||||
Hue = 12,
|
||||
Saturation = 13,
|
||||
Color = 14,
|
||||
Luminosity = 15,
|
||||
// Clip is the same as normal, but doesn't always push a blend group.
|
||||
Clip = 128,
|
||||
}
|
||||
|
||||
/// Defines the layer composition function for a blend operation.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum Compose {
|
||||
Clear = 0,
|
||||
Copy = 1,
|
||||
Dest = 2,
|
||||
SrcOver = 3,
|
||||
DestOver = 4,
|
||||
SrcIn = 5,
|
||||
DestIn = 6,
|
||||
SrcOut = 7,
|
||||
DestOut = 8,
|
||||
SrcAtop = 9,
|
||||
DestAtop = 10,
|
||||
Xor = 11,
|
||||
Plus = 12,
|
||||
PlusLighter = 13,
|
||||
}
|
||||
|
||||
/// Blend mode consisting of mixing and composition functions.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct BlendMode {
|
||||
pub mix: Mix,
|
||||
pub compose: Compose,
|
||||
}
|
||||
|
||||
impl BlendMode {
|
||||
pub fn new(mix: Mix, compose: Compose) -> Self {
|
||||
Self { mix, compose }
|
||||
}
|
||||
|
||||
pub fn pack(&self) -> u32 {
|
||||
(self.mix as u32) << 8 | self.compose as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BlendMode {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mix: Mix::Clip,
|
||||
compose: Compose::SrcOver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mix> for BlendMode {
|
||||
fn from(mix: Mix) -> Self {
|
||||
Self {
|
||||
mix,
|
||||
compose: Compose::SrcOver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Compose> for BlendMode {
|
||||
fn from(compose: Compose) -> Self {
|
||||
Self {
|
||||
mix: Mix::Normal,
|
||||
compose,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,11 +14,11 @@
|
|||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
use super::style::{Fill, Stroke};
|
||||
use super::{Affine, BlendMode, PathElement, Scene, SceneData, SceneFragment};
|
||||
use crate::{brush::*, ResourcePatch};
|
||||
use super::{conv, Scene, SceneData, SceneFragment};
|
||||
use crate::ResourcePatch;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use core::borrow::Borrow;
|
||||
use peniko::kurbo::{Affine, PathEl, Rect, Shape};
|
||||
use peniko::{BlendMode, BrushRef, ColorStop, Fill, Stroke};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// Builder for constructing a scene or scene fragment.
|
||||
|
@ -51,44 +51,43 @@ impl<'a> SceneBuilder<'a> {
|
|||
|
||||
/// Pushes a new layer bound by the specifed shape and composed with
|
||||
/// previous layers using the specified blend mode.
|
||||
pub fn push_layer<'s, E>(&mut self, blend: BlendMode, transform: Affine, elements: E)
|
||||
where
|
||||
E: IntoIterator,
|
||||
E::IntoIter: Clone,
|
||||
E::Item: Borrow<PathElement>,
|
||||
{
|
||||
pub fn push_layer(
|
||||
&mut self,
|
||||
blend: impl Into<BlendMode>,
|
||||
transform: Affine,
|
||||
shape: &impl Shape,
|
||||
) {
|
||||
let blend = blend.into();
|
||||
self.maybe_encode_transform(transform);
|
||||
self.linewidth(-1.0);
|
||||
let elements = elements.into_iter();
|
||||
self.encode_path(elements, true);
|
||||
self.begin_clip(Some(blend));
|
||||
if !self.encode_path(shape, true) {
|
||||
// If the layer shape is invalid, encode a valid empty path. This suppresses
|
||||
// all drawing until the layer is popped.
|
||||
self.encode_path(&Rect::new(0.0, 0.0, 0.0, 0.0), true);
|
||||
}
|
||||
self.begin_clip(blend);
|
||||
self.layers.push(blend);
|
||||
}
|
||||
|
||||
/// Pops the current layer.
|
||||
pub fn pop_layer(&mut self) {
|
||||
if let Some(layer) = self.layers.pop() {
|
||||
self.end_clip(Some(layer));
|
||||
if let Some(blend) = self.layers.pop() {
|
||||
self.end_clip(blend);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills a shape using the specified style and brush.
|
||||
pub fn fill<'s, E>(
|
||||
pub fn fill<'b>(
|
||||
&mut self,
|
||||
_style: Fill,
|
||||
transform: Affine,
|
||||
brush: &Brush,
|
||||
brush: impl Into<BrushRef<'b>>,
|
||||
brush_transform: Option<Affine>,
|
||||
elements: E,
|
||||
) where
|
||||
E: IntoIterator,
|
||||
E::IntoIter: Clone,
|
||||
E::Item: Borrow<PathElement>,
|
||||
{
|
||||
shape: &impl Shape,
|
||||
) {
|
||||
self.maybe_encode_transform(transform);
|
||||
self.linewidth(-1.0);
|
||||
let elements = elements.into_iter();
|
||||
if self.encode_path(elements, true) {
|
||||
if self.encode_path(shape, true) {
|
||||
if let Some(brush_transform) = brush_transform {
|
||||
self.encode_transform(transform * brush_transform);
|
||||
self.swap_last_tags();
|
||||
|
@ -100,23 +99,17 @@ impl<'a> SceneBuilder<'a> {
|
|||
}
|
||||
|
||||
/// Strokes a shape using the specified style and brush.
|
||||
pub fn stroke<'s, D, E>(
|
||||
pub fn stroke<'b>(
|
||||
&mut self,
|
||||
style: &Stroke<D>,
|
||||
style: &Stroke,
|
||||
transform: Affine,
|
||||
brush: &Brush,
|
||||
brush: impl Into<BrushRef<'b>>,
|
||||
brush_transform: Option<Affine>,
|
||||
elements: E,
|
||||
) where
|
||||
D: Borrow<[f32]>,
|
||||
E: IntoIterator,
|
||||
E::IntoIter: Clone,
|
||||
E::Item: Borrow<PathElement>,
|
||||
{
|
||||
shape: &impl Shape,
|
||||
) {
|
||||
self.maybe_encode_transform(transform);
|
||||
self.linewidth(style.width);
|
||||
let elements = elements.into_iter();
|
||||
if self.encode_path(elements, false) {
|
||||
if self.encode_path(shape, false) {
|
||||
if let Some(brush_transform) = brush_transform {
|
||||
self.encode_transform(transform * brush_transform);
|
||||
self.swap_last_tags();
|
||||
|
@ -134,30 +127,39 @@ impl<'a> SceneBuilder<'a> {
|
|||
|
||||
/// Completes construction and finalizes the underlying scene.
|
||||
pub fn finish(mut self) {
|
||||
while let Some(layer) = self.layers.pop() {
|
||||
self.end_clip(Some(layer));
|
||||
while !self.layers.is_empty() {
|
||||
self.pop_layer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SceneBuilder<'a> {
|
||||
fn encode_path<E>(&mut self, elements: E, is_fill: bool) -> bool
|
||||
where
|
||||
E: Iterator,
|
||||
E::Item: Borrow<PathElement>,
|
||||
{
|
||||
/// Encodes a path for the specified shape.
|
||||
///
|
||||
/// When the `is_fill` parameter is true, closes any open subpaths by inserting
|
||||
/// a line to the start point of the subpath with the end segment bit set.
|
||||
fn encode_path(&mut self, shape: &impl Shape, is_fill: bool) -> bool {
|
||||
let mut b = PathBuilder::new(
|
||||
&mut self.scene.tag_stream,
|
||||
&mut self.scene.pathseg_stream,
|
||||
is_fill,
|
||||
);
|
||||
for el in elements {
|
||||
match el.borrow() {
|
||||
PathElement::MoveTo(p0) => b.move_to(p0.x, p0.y),
|
||||
PathElement::LineTo(p0) => b.line_to(p0.x, p0.y),
|
||||
PathElement::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y),
|
||||
PathElement::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y),
|
||||
PathElement::Close => b.close_path(),
|
||||
for el in shape.path_elements(0.1) {
|
||||
match el {
|
||||
PathEl::MoveTo(p0) => b.move_to(p0.x as f32, p0.y as f32),
|
||||
PathEl::LineTo(p0) => b.line_to(p0.x as f32, p0.y as f32),
|
||||
PathEl::QuadTo(p0, p1) => {
|
||||
b.quad_to(p0.x as f32, p0.y as f32, p1.x as f32, p1.y as f32)
|
||||
}
|
||||
PathEl::CurveTo(p0, p1, p2) => b.cubic_to(
|
||||
p0.x as f32,
|
||||
p0.y as f32,
|
||||
p1.x as f32,
|
||||
p1.y as f32,
|
||||
p2.x as f32,
|
||||
p2.y as f32,
|
||||
),
|
||||
PathEl::ClosePath => b.close_path(),
|
||||
}
|
||||
}
|
||||
b.finish();
|
||||
|
@ -171,14 +173,16 @@ impl<'a> SceneBuilder<'a> {
|
|||
}
|
||||
|
||||
fn maybe_encode_transform(&mut self, transform: Affine) {
|
||||
if self.scene.transform_stream.last() != Some(&transform) {
|
||||
if self.scene.transform_stream.last() != Some(&conv::affine_to_f32(&transform)) {
|
||||
self.encode_transform(transform);
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_transform(&mut self, transform: Affine) {
|
||||
self.scene.tag_stream.push(0x20);
|
||||
self.scene.transform_stream.push(transform);
|
||||
self.scene
|
||||
.transform_stream
|
||||
.push(conv::affine_to_f32(&transform));
|
||||
}
|
||||
|
||||
// Swap the last two tags in the tag stream; used for transformed
|
||||
|
@ -196,45 +200,44 @@ impl<'a> SceneBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_brush(&mut self, brush: &Brush) {
|
||||
match brush {
|
||||
Brush::Solid(color) => {
|
||||
fn encode_brush<'b>(&mut self, brush: impl Into<BrushRef<'b>>) {
|
||||
match brush.into() {
|
||||
BrushRef::Solid(color) => {
|
||||
self.scene.drawtag_stream.push(DRAWTAG_FILLCOLOR);
|
||||
let rgba_color = color.to_premul_u32();
|
||||
self.scene
|
||||
.drawdata_stream
|
||||
.extend(bytemuck::bytes_of(&FillColor { rgba_color }));
|
||||
}
|
||||
Brush::LinearGradient(gradient) => {
|
||||
BrushRef::LinearGradient(gradient) => {
|
||||
let index = self.add_ramp(&gradient.stops);
|
||||
self.scene.drawtag_stream.push(DRAWTAG_FILLLINGRADIENT);
|
||||
self.scene
|
||||
.drawdata_stream
|
||||
.extend(bytemuck::bytes_of(&FillLinGradient {
|
||||
index,
|
||||
p0: [gradient.start.x, gradient.start.y],
|
||||
p1: [gradient.end.x, gradient.end.y],
|
||||
p0: conv::point_to_f32(gradient.start),
|
||||
p1: conv::point_to_f32(gradient.end),
|
||||
}));
|
||||
}
|
||||
Brush::RadialGradient(gradient) => {
|
||||
BrushRef::RadialGradient(gradient) => {
|
||||
let index = self.add_ramp(&gradient.stops);
|
||||
self.scene.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT);
|
||||
self.scene
|
||||
.drawdata_stream
|
||||
.extend(bytemuck::bytes_of(&FillRadGradient {
|
||||
index,
|
||||
p0: [gradient.center0.x, gradient.center0.y],
|
||||
p1: [gradient.center1.x, gradient.center1.y],
|
||||
r0: gradient.radius0,
|
||||
r1: gradient.radius1,
|
||||
p0: conv::point_to_f32(gradient.start_center),
|
||||
p1: conv::point_to_f32(gradient.end_center),
|
||||
r0: gradient.start_radius,
|
||||
r1: gradient.end_radius,
|
||||
}));
|
||||
}
|
||||
Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"),
|
||||
Brush::Image(_image) => todo!("images aren't done yet!"),
|
||||
BrushRef::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 {
|
||||
fn add_ramp(&mut self, stops: &[ColorStop]) -> u32 {
|
||||
let offset = self.scene.drawdata_stream.len();
|
||||
let resources = &mut self.scene.resources;
|
||||
let stops_start = resources.stops.len();
|
||||
|
@ -247,10 +250,10 @@ impl<'a> SceneBuilder<'a> {
|
|||
}
|
||||
|
||||
/// Start a clip.
|
||||
fn begin_clip(&mut self, blend: Option<BlendMode>) {
|
||||
fn begin_clip(&mut self, blend: BlendMode) {
|
||||
self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP);
|
||||
let element = Clip {
|
||||
blend: blend.unwrap_or(BlendMode::default()).pack(),
|
||||
blend: encode_blend_mode(blend),
|
||||
};
|
||||
self.scene
|
||||
.drawdata_stream
|
||||
|
@ -258,10 +261,10 @@ impl<'a> SceneBuilder<'a> {
|
|||
self.scene.n_clip += 1;
|
||||
}
|
||||
|
||||
fn end_clip(&mut self, blend: Option<BlendMode>) {
|
||||
fn end_clip(&mut self, blend: BlendMode) {
|
||||
self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP);
|
||||
let element = Clip {
|
||||
blend: blend.unwrap_or(BlendMode::default()).pack(),
|
||||
blend: encode_blend_mode(blend),
|
||||
};
|
||||
self.scene
|
||||
.drawdata_stream
|
||||
|
@ -273,6 +276,10 @@ impl<'a> SceneBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_blend_mode(mode: BlendMode) -> u32 {
|
||||
(mode.mix as u32) << 8 | mode.compose as u32
|
||||
}
|
||||
|
||||
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
|
||||
const DRAWTAG_FILLCOLOR: u32 = 0x44;
|
||||
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;
|
||||
|
@ -371,6 +378,12 @@ impl<'a> PathBuilder<'a> {
|
|||
|
||||
pub fn line_to(&mut self, x: f32, y: f32) {
|
||||
if self.state == PathState::Start {
|
||||
if self.n_pathseg == 0 {
|
||||
// This copies the behavior of kurbo which treats an initial line, quad
|
||||
// or curve as a move.
|
||||
self.move_to(x, y);
|
||||
return;
|
||||
}
|
||||
self.move_to(self.first_pt[0], self.first_pt[1]);
|
||||
}
|
||||
let buf = [x, y];
|
||||
|
@ -383,6 +396,10 @@ impl<'a> PathBuilder<'a> {
|
|||
|
||||
pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
|
||||
if self.state == PathState::Start {
|
||||
if self.n_pathseg == 0 {
|
||||
self.move_to(x2, y2);
|
||||
return;
|
||||
}
|
||||
self.move_to(self.first_pt[0], self.first_pt[1]);
|
||||
}
|
||||
let buf = [x1, y1, x2, y2];
|
||||
|
@ -395,6 +412,10 @@ impl<'a> PathBuilder<'a> {
|
|||
|
||||
pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) {
|
||||
if self.state == PathState::Start {
|
||||
if self.n_pathseg == 0 {
|
||||
self.move_to(x3, y3);
|
||||
return;
|
||||
}
|
||||
self.move_to(self.first_pt[0], self.first_pt[1]);
|
||||
}
|
||||
let buf = [x1, y1, x2, y2, x3, y3];
|
||||
|
|
|
@ -14,22 +14,19 @@
|
|||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
mod blend;
|
||||
mod builder;
|
||||
mod style;
|
||||
mod resource;
|
||||
|
||||
pub use blend::{BlendMode, Compose, Mix};
|
||||
pub use builder::SceneBuilder;
|
||||
pub use style::*;
|
||||
pub use resource::{ResourceBundle, ResourcePatch};
|
||||
|
||||
use super::geometry::{Affine, Point};
|
||||
use super::path::PathElement;
|
||||
use super::resource::{ResourceBundle, ResourcePatch};
|
||||
use super::conv;
|
||||
use peniko::kurbo::Affine;
|
||||
|
||||
/// Raw data streams describing an encoded scene.
|
||||
#[derive(Default)]
|
||||
pub struct SceneData {
|
||||
pub transform_stream: Vec<Affine>,
|
||||
pub transform_stream: Vec<[f32; 6]>,
|
||||
pub tag_stream: Vec<u8>,
|
||||
pub pathseg_stream: Vec<u8>,
|
||||
pub linewidth_stream: Vec<f32>,
|
||||
|
@ -58,8 +55,7 @@ impl SceneData {
|
|||
self.n_clip = 0;
|
||||
self.resources.clear();
|
||||
if !is_fragment {
|
||||
self.transform_stream
|
||||
.push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]));
|
||||
self.transform_stream.push([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
|
||||
self.linewidth_stream.push(-1.0);
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +64,12 @@ impl SceneData {
|
|||
let stops_base = self.resources.stops.len();
|
||||
let drawdata_base = self.drawdata_stream.len();
|
||||
if let Some(transform) = *transform {
|
||||
self.transform_stream
|
||||
.extend(other.transform_stream.iter().map(|x| transform * *x));
|
||||
self.transform_stream.extend(
|
||||
other
|
||||
.transform_stream
|
||||
.iter()
|
||||
.map(|x| conv::affine_to_f32(&(transform * conv::affine_from_f32(x)))),
|
||||
);
|
||||
} else {
|
||||
self.transform_stream
|
||||
.extend_from_slice(&other.transform_stream);
|
||||
|
@ -127,9 +127,8 @@ impl SceneFragment {
|
|||
self.data.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the underlying stream of points that defined all encoded path
|
||||
/// segments.
|
||||
pub fn points(&self) -> &[Point] {
|
||||
/// Returns the the entire sequence of points in the scene fragment.
|
||||
pub fn points(&self) -> &[[f32; 2]] {
|
||||
if self.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use crate::brush::GradientStop;
|
||||
use core::ops::Range;
|
||||
use peniko::ColorStop;
|
||||
|
||||
#[derive(Default)]
|
||||
/// Collection of late bound resources for a scene or scene fragment.
|
||||
pub struct ResourceBundle {
|
||||
/// Sequence of resource patches.
|
||||
pub patches: Vec<ResourcePatch>,
|
||||
/// Cache of gradient stops, referenced by range from the patches.
|
||||
pub stops: Vec<GradientStop>,
|
||||
/// Cache of color stops, referenced by range from the patches.
|
||||
pub stops: Vec<ColorStop>,
|
||||
}
|
||||
|
||||
impl ResourceBundle {
|
|
@ -1,71 +0,0 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
/// Describes the winding rule that determines the interior portion of a path.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Fill {
|
||||
NonZero,
|
||||
EvenOdd,
|
||||
}
|
||||
|
||||
/// Defines the connection between two segments of a stroke.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Join {
|
||||
/// A straight line connecting the segments.
|
||||
Bevel,
|
||||
/// The segments are extended to their natural intersection point.
|
||||
Miter,
|
||||
/// An arc between the segments.
|
||||
Round,
|
||||
}
|
||||
|
||||
/// Defines the shape to be drawn at the ends of a stroke.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Cap {
|
||||
/// Flat cap.
|
||||
Butt,
|
||||
/// Square cap with dimensions equal to half the stroke width.
|
||||
Square,
|
||||
/// Rounded cap with radius equal to half the stroke width.
|
||||
Round,
|
||||
}
|
||||
|
||||
/// Describes the visual style of a stroke.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Stroke<D>
|
||||
where
|
||||
D: Borrow<[f32]>,
|
||||
{
|
||||
/// Width of the stroke.
|
||||
pub width: f32,
|
||||
/// Style for connecting segments of the stroke.
|
||||
pub join: Join,
|
||||
/// Limit for miter joins.
|
||||
pub miter_limit: f32,
|
||||
/// Style for capping the beginning of an open subpath.
|
||||
pub start_cap: Cap,
|
||||
/// Style for capping the end of an open subpath.
|
||||
pub end_cap: Cap,
|
||||
/// Lengths of dashes in alternating on/off order.
|
||||
pub dash_pattern: D,
|
||||
/// Offset of the first dash.
|
||||
pub dash_offset: f32,
|
||||
/// True if the stroke width should be affected by the scale of a
|
||||
/// transform.
|
||||
pub scale: bool,
|
||||
}
|
|
@ -9,13 +9,12 @@ edition = "2021"
|
|||
wgpu = "0.14"
|
||||
env_logger = "0.9.1"
|
||||
pollster = "0.2.5"
|
||||
futures-intrusive = "0.4.1"
|
||||
futures-intrusive = "0.5.0"
|
||||
parking_lot = "0.12"
|
||||
bytemuck = { version = "1.12.1", features = ["derive"] }
|
||||
png = "0.17.6"
|
||||
|
||||
piet-scene = { path = "../piet-scene", features = ["kurbo"] }
|
||||
piet-scene = { path = "../piet-scene" }
|
||||
|
||||
# for picosvg, should be split out
|
||||
roxmltree = "0.13"
|
||||
kurbo = "0.8.3"
|
||||
|
|
|
@ -45,11 +45,11 @@ fn color_burn(cb: f32, cs: f32) -> f32 {
|
|||
}
|
||||
|
||||
fn hard_light(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> {
|
||||
return mix(
|
||||
screen(cb, 2.0 * cs - 1.0),
|
||||
cb * 2.0 * cs,
|
||||
return mix(
|
||||
screen(cb, 2.0 * cs - 1.0),
|
||||
cb * 2.0 * cs,
|
||||
vec3<f32>(cs <= vec3<f32>(0.5))
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
fn soft_light(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> {
|
||||
|
|
|
@ -4,8 +4,7 @@ use std::str::FromStr;
|
|||
|
||||
use roxmltree::{Document, Node};
|
||||
|
||||
use kurbo::{Affine, BezPath};
|
||||
|
||||
use piet_scene::kurbo::{Affine, BezPath};
|
||||
use piet_scene::Color;
|
||||
|
||||
pub struct PicoSvg {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use piet_scene::{Color, GradientStop, GradientStops};
|
||||
use piet_scene::{Color, ColorStop, ColorStops};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -8,7 +8,7 @@ const RETAINED_COUNT: usize = 64;
|
|||
#[derive(Default)]
|
||||
pub struct RampCache {
|
||||
epoch: u64,
|
||||
map: HashMap<GradientStops, (u32, u64)>,
|
||||
map: HashMap<ColorStops, (u32, u64)>,
|
||||
data: Vec<u32>,
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ impl RampCache {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, stops: &[GradientStop]) -> u32 {
|
||||
pub fn add(&mut self, stops: &[ColorStop]) -> u32 {
|
||||
if let Some(entry) = self.map.get_mut(stops) {
|
||||
entry.1 = self.epoch;
|
||||
entry.0
|
||||
|
@ -72,7 +72,7 @@ impl RampCache {
|
|||
}
|
||||
}
|
||||
|
||||
fn make_ramp<'a>(stops: &'a [GradientStop]) -> impl Iterator<Item = u32> + 'a {
|
||||
fn make_ramp<'a>(stops: &'a [ColorStop]) -> impl Iterator<Item = u32> + 'a {
|
||||
let mut last_u = 0.0;
|
||||
let mut last_c = ColorF64::from_color(stops[0].color);
|
||||
let mut this_u = last_u;
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
use kurbo::BezPath;
|
||||
use piet_scene::kurbo::{Affine, Ellipse, PathEl, Point, Rect};
|
||||
use piet_scene::{
|
||||
Affine, BlendMode, Brush, Color, Compose, ExtendMode, Fill, GradientStop, LinearGradient, Mix,
|
||||
PathElement, Point, RadialGradient, Rect, Scene, SceneBuilder, SceneFragment, Stroke,
|
||||
BlendMode, Brush, Color, Fill, LinearGradient, Mix, RadialGradient, Scene, SceneBuilder,
|
||||
SceneFragment, Stroke,
|
||||
};
|
||||
|
||||
use crate::pico_svg::PicoSvg;
|
||||
|
@ -29,20 +29,19 @@ pub fn gen_test_scene() -> Scene {
|
|||
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,
|
||||
PathEl::MoveTo(Point::new(100.0, 100.0)),
|
||||
PathEl::LineTo(Point::new(500.0, 120.0)),
|
||||
PathEl::LineTo(Point::new(300.0, 150.0)),
|
||||
PathEl::LineTo(Point::new(200.0, 260.0)),
|
||||
PathEl::LineTo(Point::new(150.0, 210.0)),
|
||||
];
|
||||
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 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 transform = Affine::translate((100.0, 100.0));
|
||||
let style = Stroke::new(1.0);
|
||||
let brush = Brush::Solid(Color::rgb8(0xa0, 0x00, 0x00));
|
||||
builder.stroke(&style, transform, &brush, None, &path);
|
||||
}
|
||||
|
@ -80,18 +79,18 @@ pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
|||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
&fill.color.into(),
|
||||
fill.color,
|
||||
None,
|
||||
convert_bez_path(&fill.path),
|
||||
&fill.path,
|
||||
);
|
||||
}
|
||||
Item::Stroke(stroke) => {
|
||||
sb.stroke(
|
||||
&simple_stroke(stroke.width as f32),
|
||||
&Stroke::new(stroke.width as f32),
|
||||
Affine::IDENTITY,
|
||||
&stroke.color.into(),
|
||||
stroke.color,
|
||||
None,
|
||||
convert_bez_path(&stroke.path),
|
||||
&stroke.path,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -101,25 +100,6 @@ pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
||||
const BLEND_MODES: &[Mix] = &[
|
||||
|
@ -143,7 +123,7 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
|||
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 transform = Affine::translate((i as f64 * 225., j as f64 * 225.));
|
||||
let square = blend_square(blend.into());
|
||||
sb.append(&square, Some(transform));
|
||||
}
|
||||
|
@ -152,25 +132,10 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
|||
#[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)] = &[
|
||||
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)),
|
||||
|
@ -178,62 +143,30 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin
|
|||
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());
|
||||
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.into(), transform, rect.elements());
|
||||
sb.push_layer(Mix::Normal, transform, &rect);
|
||||
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());
|
||||
let linear = LinearGradient::new((0.0, 0.0), (0.0, 200.0)).stops([Color::WHITE, *c]);
|
||||
sb.push_layer(blend, transform, &rect);
|
||||
// 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.);
|
||||
* 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,
|
||||
make_ellipse(100., 100., 90., 90.),
|
||||
&Ellipse::new((100., 100.), (90., 90.), 0.),
|
||||
);
|
||||
sb.pop_layer();
|
||||
}
|
||||
|
@ -248,34 +181,3 @@ fn blend_square(blend: BlendMode) -> SceneFragment {
|
|||
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])
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue