pnte 0.3.3

2D Graphics library for Windows in Rust
Documentation
use crate::*;
use windows::Win32::Graphics::{Direct2D::Common::*, Direct2D::*};
use windows_numerics::Vector2;

#[derive(Clone, Copy, Debug)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct QuadraticBezierSegment {
    pub ctrl: Point<f32>,
    pub to: Point<f32>,
}

impl QuadraticBezierSegment {
    #[inline]
    pub fn new(ctrl: impl Into<Point<f32>>, to: impl Into<Point<f32>>) -> Self {
        Self {
            ctrl: ctrl.into(),
            to: to.into(),
        }
    }
}

impl From<QuadraticBezierSegment> for D2D1_QUADRATIC_BEZIER_SEGMENT {
    #[inline]
    fn from(value: QuadraticBezierSegment) -> Self {
        Self {
            point1: value.ctrl.into(),
            point2: value.to.into(),
        }
    }
}

#[derive(Clone, Copy, Debug)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CubicBezierSegment {
    pub c0: Point<f32>,
    pub c1: Point<f32>,
    pub to: Point<f32>,
}

impl CubicBezierSegment {
    #[inline]
    pub fn new(
        c0: impl Into<Point<f32>>,
        c1: impl Into<Point<f32>>,
        to: impl Into<Point<f32>>,
    ) -> Self {
        Self {
            c0: c0.into(),
            c1: c1.into(),
            to: to.into(),
        }
    }
}

impl From<CubicBezierSegment> for D2D1_BEZIER_SEGMENT {
    #[inline]
    fn from(value: CubicBezierSegment) -> Self {
        Self {
            point1: value.c0.into(),
            point2: value.c1.into(),
            point3: value.to.into(),
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum PathEnd {
    Open = D2D1_FIGURE_END_OPEN.0,
    Closed = D2D1_FIGURE_END_CLOSED.0,
}

impl From<PathEnd> for D2D1_FIGURE_END {
    #[inline]
    fn from(value: PathEnd) -> Self {
        Self(value as i32)
    }
}

pub struct PathBuilder {
    geometry: ID2D1PathGeometry,
    sink: ID2D1GeometrySink,
}

impl PathBuilder {
    #[inline]
    pub fn line_to(self, point: impl Into<Point<f32>>) -> Self {
        let point: Point<f32> = point.into();
        unsafe { self.sink.AddLine(point.into()) }
        self
    }

    #[inline]
    pub fn add_lines(self, points: &[Point<f32>]) -> Self {
        unsafe {
            let lines = std::slice::from_raw_parts(points.as_ptr() as *const Vector2, points.len());
            self.sink.AddLines(lines);
        }
        self
    }

    #[inline]
    pub fn quadratic_bezier_to(
        self,
        ctrl: impl Into<Point<f32>>,
        to: impl Into<Point<f32>>,
    ) -> Self {
        let segment = QuadraticBezierSegment::new(ctrl, to);
        unsafe {
            self.sink.AddQuadraticBezier(&segment.into());
        }
        self
    }

    #[inline]
    pub fn add_quadratic_beziers(self, segments: &[QuadraticBezierSegment]) -> Self {
        unsafe {
            let segments = std::slice::from_raw_parts(
                segments.as_ptr() as *const D2D1_QUADRATIC_BEZIER_SEGMENT,
                segments.len(),
            );
            self.sink.AddQuadraticBeziers(segments);
        }
        self
    }

    #[inline]
    pub fn cubic_bezier_to(
        self,
        c0: impl Into<Point<f32>>,
        c1: impl Into<Point<f32>>,
        to: impl Into<Point<f32>>,
    ) -> Self {
        let segment = CubicBezierSegment::new(c0, c1, to);
        unsafe {
            self.sink.AddBezier(&segment.into());
        }
        self
    }

    #[inline]
    pub fn add_cubic_beziers(self, segments: &[CubicBezierSegment]) -> Self {
        unsafe {
            let segments = std::slice::from_raw_parts(
                segments.as_ptr() as *const D2D1_BEZIER_SEGMENT,
                segments.len(),
            );
            self.sink.AddBeziers(segments);
        }
        self
    }

    #[inline]
    pub fn build(self, end: PathEnd) -> Result<Path> {
        unsafe {
            self.sink.EndFigure(end.into());
            self.sink.Close()?;
        }
        Ok(Path(self.geometry))
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Path(ID2D1PathGeometry);

impl Path {
    #[inline]
    pub fn builder<T>(ctx: &Context<T>, start: impl Into<Point<f32>>) -> Result<PathBuilder>
    where
        T: Backend,
    {
        let geometry = unsafe { ctx.backend.d2d1_factory().CreatePathGeometry()? };
        let start: Point<f32> = start.into();
        let sink = unsafe { geometry.Open()? };
        unsafe {
            sink.BeginFigure(start.into(), D2D1_FIGURE_BEGIN_FILLED);
            sink.SetFillMode(D2D1_FILL_MODE_WINDING);
        }
        Ok(PathBuilder {
            geometry: geometry.into(),
            sink,
        })
    }
}

impl Fill for Path {
    #[inline]
    fn fill(&self, dc: &ID2D1DeviceContext5, brush: &ID2D1Brush) {
        unsafe {
            dc.FillGeometry(&self.0, brush, None);
        }
    }
}

impl Stroke for Path {
    #[inline]
    fn stroke(
        &self,
        dc: &ID2D1DeviceContext5,
        brush: &ID2D1Brush,
        width: f32,
        style: Option<&ID2D1StrokeStyle1>,
    ) {
        unsafe {
            dc.DrawGeometry(&self.0, brush, width, style.map(|s| s.into()));
        }
    }
}