1use yog_data::Schema;
6
7use crate::color::Color;
8use crate::image::Image;
9use crate::math::Vec2;
10use crate::path::{self, Seg};
11use crate::raster::Canvas;
12use crate::rect::Rect;
13
14#[derive(Schema, Debug, Clone, PartialEq)]
16#[data(tag = "op")]
17pub enum Op {
18 Fill(Fill),
20 Stroke(Stroke),
22 Image(ImageOp),
24}
25
26#[derive(Schema, Debug, Clone, PartialEq)]
27pub struct Fill {
28 pub path: Vec<Seg>,
29 pub color: Option<Color>,
31}
32
33#[derive(Schema, Debug, Clone, PartialEq)]
34pub struct Stroke {
35 pub path: Vec<Seg>,
36 pub width: f32,
38 pub color: Option<Color>,
40}
41
42#[derive(Schema, Debug, Clone, PartialEq)]
44pub struct ImageOp {
45 pub rect: Rect,
46 pub image: Image,
47 #[data(default)]
50 pub tinted: bool,
51}
52
53pub fn bounds(ops: &[Op]) -> Option<Rect> {
56 ops.iter()
57 .filter_map(|op| match op {
58 Op::Fill(f) => path::bounds(&f.path, 0.0),
59 Op::Stroke(s) => path::bounds(&s.path, s.width / 2.0),
60 Op::Image(i) => Some(i.rect),
61 })
62 .reduce(|a, b| a.union(&b))
63}
64
65impl Canvas {
66 pub fn draw(&mut self, ops: &[Op], to_px: impl Fn(Vec2) -> Vec2, scale: f32, current: Color) {
70 for op in ops {
71 match op {
72 Op::Fill(f) => {
73 self.fill(&path::flatten(&f.path, &to_px), f.color.unwrap_or(current))
74 }
75 Op::Stroke(s) => self.stroke(
76 &path::flatten(&s.path, &to_px),
77 s.width * scale,
78 s.color.unwrap_or(current),
79 ),
80 Op::Image(i) => {
81 let r = i.rect;
82 let dst = Rect::from_corners(
83 to_px(Vec2::new(r.x, r.y)),
84 to_px(Vec2::new(r.right(), r.bottom())),
85 );
86 self.image(&i.image, dst, i.tinted.then_some(current));
87 }
88 }
89 }
90 }
91}