use yog_data::Schema;
use crate::color::Color;
use crate::image::Image;
use crate::mask::Mask;
use crate::math::Vec2;
use crate::path::{self, Seg};
use crate::raster::Canvas;
use crate::rect::Rect;
#[derive(Schema, Debug, Clone, PartialEq)]
#[data(tag = "op")]
pub enum Op {
Fill(Fill),
Stroke(Stroke),
Image(ImageOp),
Mask(MaskOp),
}
#[derive(Schema, Debug, Clone, PartialEq)]
pub struct Fill {
pub path: Vec<Seg>,
pub color: Option<Color>,
}
#[derive(Schema, Debug, Clone, PartialEq)]
pub struct Stroke {
pub path: Vec<Seg>,
pub width: f32,
pub color: Option<Color>,
}
#[derive(Schema, Debug, Clone, PartialEq)]
pub struct ImageOp {
pub rect: Rect,
pub image: Image,
#[data(default)]
pub tinted: bool,
}
#[derive(Schema, Debug, Clone, PartialEq)]
pub struct MaskOp {
pub rect: Rect,
pub mask: Mask,
pub color: Option<Color>,
}
pub fn bounds(ops: &[Op]) -> Option<Rect> {
ops.iter()
.filter_map(|op| match op {
Op::Fill(f) => path::bounds(&f.path, 0.0),
Op::Stroke(s) => path::bounds(&s.path, s.width / 2.0),
Op::Image(i) => Some(i.rect),
Op::Mask(m) => Some(m.rect),
})
.reduce(|a, b| a.union(&b))
}
impl Canvas {
pub fn draw(&mut self, ops: &[Op], to_px: impl Fn(Vec2) -> Vec2, scale: f32, current: Color) {
for op in ops {
match op {
Op::Fill(f) => {
self.fill(&path::flatten(&f.path, &to_px), f.color.unwrap_or(current))
}
Op::Stroke(s) => self.stroke(
&path::flatten(&s.path, &to_px),
s.width * scale,
s.color.unwrap_or(current),
),
Op::Image(i) => {
self.image(
&i.image,
px_box(i.rect, &to_px),
i.tinted.then_some(current),
);
}
Op::Mask(m) => {
self.mask(&m.mask, px_box(m.rect, &to_px), m.color.unwrap_or(current))
}
}
}
}
}
fn px_box(r: Rect, to_px: &impl Fn(Vec2) -> Vec2) -> Rect {
Rect::from_corners(
to_px(Vec2::new(r.x, r.y)),
to_px(Vec2::new(r.right(), r.bottom())),
)
}