hydrolysis 0.1.0

A modern UI framework for Rust
use super::{Brush, DrawContext};
use vello::kurbo::{
    Affine, BezPath, Circle, Line, Point, Rect, RoundedRect, RoundedRectRadii, Shape,
};

pub struct VelloDrawContext<'a> {
    scene: &'a mut vello::Scene,
    transform_stack: Vec<Affine>,
}

impl<'a> VelloDrawContext<'a> {
    pub fn with_root_transform(scene: &'a mut vello::Scene, transform: Affine) -> Self {
        Self {
            scene,
            transform_stack: vec![Affine::IDENTITY, transform],
        }
    }

    fn transform(&self) -> Affine {
        *self
            .transform_stack
            .last()
            .expect("vello draw context transform stack is empty")
    }

    fn fill_shape(&mut self, shape: &impl Shape, brush: &Brush) {
        match brush {
            Brush::Solid(color) => {
                self.scene.fill(
                    vello::peniko::Fill::NonZero,
                    self.transform(),
                    color,
                    None,
                    shape,
                );
            }
            Brush::Gradient(gradient) => {
                self.scene.fill(
                    vello::peniko::Fill::NonZero,
                    self.transform(),
                    gradient,
                    None,
                    shape,
                );
            }
        }
    }

    fn stroke_shape(&mut self, shape: &impl Shape, brush: &Brush, width: f64) {
        let stroke = vello::kurbo::Stroke::new(width);
        match brush {
            Brush::Solid(color) => {
                self.scene
                    .stroke(&stroke, self.transform(), color, None, shape);
            }
            Brush::Gradient(gradient) => {
                self.scene
                    .stroke(&stroke, self.transform(), gradient, None, shape);
            }
        }
    }
}

impl DrawContext for VelloDrawContext<'_> {
    fn fill_rect(&mut self, rect: Rect, brush: &Brush) {
        self.fill_shape(&rect, brush);
    }

    fn fill_rounded_rect(&mut self, rect: Rect, radii: RoundedRectRadii, brush: &Brush) {
        let rounded = RoundedRect::from_rect(rect, radii);
        self.fill_shape(&rounded, brush);
    }

    fn stroke_rect(&mut self, rect: Rect, brush: &Brush, width: f64) {
        self.stroke_shape(&rect, brush, width);
    }

    fn stroke_rounded_rect(
        &mut self,
        rect: Rect,
        radii: RoundedRectRadii,
        brush: &Brush,
        width: f64,
    ) {
        let rounded = RoundedRect::from_rect(rect, radii);
        self.stroke_shape(&rounded, brush, width);
    }

    fn stroke_line(&mut self, from: Point, to: Point, brush: &Brush, width: f64) {
        let line = Line::new(from, to);
        self.stroke_shape(&line, brush, width);
    }

    fn stroke_circle(&mut self, center: Point, radius: f64, brush: &Brush, width: f64) {
        let circle = Circle::new(center, radius);
        self.stroke_shape(&circle, brush, width);
    }

    fn fill_circle(&mut self, center: Point, radius: f64, brush: &Brush) {
        let circle = Circle::new(center, radius);
        self.fill_shape(&circle, brush);
    }

    fn fill_path(&mut self, path: &BezPath, brush: &Brush) {
        self.fill_shape(path, brush);
    }

    fn stroke_path(&mut self, path: &BezPath, brush: &Brush, width: f64) {
        self.stroke_shape(path, brush, width);
    }

    fn push_layer(&mut self, alpha: f32, clip: Option<&Rect>) {
        let clip = clip
            .copied()
            .unwrap_or(Rect::new(-1.0e9, -1.0e9, 1.0e9, 1.0e9));
        self.scene.push_layer(
            vello::peniko::Fill::NonZero,
            vello::peniko::BlendMode::default(),
            alpha,
            self.transform(),
            &clip,
        );
    }

    fn push_rounded_layer(&mut self, alpha: f32, clip: Rect, radii: RoundedRectRadii) {
        let clip = RoundedRect::from_rect(clip, radii);
        self.scene.push_layer(
            vello::peniko::Fill::NonZero,
            vello::peniko::BlendMode::default(),
            alpha,
            self.transform(),
            &clip,
        );
    }

    fn pop_layer(&mut self) {
        self.scene.pop_layer();
    }

    fn push_transform(&mut self, affine: Affine) {
        let current = self.transform();
        self.transform_stack.push(current * affine);
    }

    fn pop_transform(&mut self) {
        assert!(
            self.transform_stack.len() > 1,
            "vello draw context transform stack underflow"
        );
        self.transform_stack.pop();
    }
}