pnte 0.3.3

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

pub trait Brush {
    fn handle(&self) -> &ID2D1Brush;
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SolidColorBrush(ID2D1Brush);

impl SolidColorBrush {
    #[inline]
    pub fn new<T: Backend>(ctx: &Context<T>, color: impl Into<Rgba>) -> Result<Self> {
        let handle = unsafe {
            let color: Rgba = color.into();
            ctx.d2d1_device_context
                .CreateSolidColorBrush(&color.into(), None)?
        };
        Ok(Self(handle.cast().unwrap()))
    }
}

impl Brush for SolidColorBrush {
    #[inline]
    fn handle(&self) -> &ID2D1Brush {
        &self.0
    }
}

#[derive(Clone, Copy, Debug)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GradientStop {
    pub position: f32,
    pub color: Rgba,
}

impl GradientStop {
    #[inline]
    pub fn new(position: f32, color: impl Into<Rgba>) -> Self {
        Self {
            position,
            color: color.into(),
        }
    }
}

impl From<GradientStop> for D2D1_GRADIENT_STOP {
    #[inline]
    fn from(value: GradientStop) -> Self {
        Self {
            position: value.position,
            color: value.color.into(),
        }
    }
}

impl<C> From<(f32, C)> for GradientStop
where
    C: Into<Rgba>,
{
    #[inline]
    fn from(value: (f32, C)) -> Self {
        Self {
            position: value.0,
            color: value.1.into(),
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GradientMode {
    Clamp = D2D1_EXTEND_MODE_CLAMP.0,
    Mirror = D2D1_EXTEND_MODE_MIRROR.0,
    Wrap = D2D1_EXTEND_MODE_WRAP.0,
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct LinearGradientBrush(ID2D1Brush);

impl LinearGradientBrush {
    #[inline]
    pub fn new<T, G>(
        ctx: &Context<T>,
        start: impl Into<Point<f32>>,
        end: impl Into<Point<f32>>,
        mode: GradientMode,
        stops: &[G],
    ) -> Result<Self>
    where
        T: Backend,
        G: Into<GradientStop> + Clone,
    {
        let dc = &ctx.d2d1_device_context;
        let stops: Vec<D2D1_GRADIENT_STOP> = stops
            .iter()
            .cloned()
            .map(|stop| D2D1_GRADIENT_STOP::from(stop.into()))
            .collect();
        let stops = unsafe {
            dc.CreateGradientStopCollection(
                &stops,
                D2D1_COLOR_SPACE_SRGB,
                D2D1_COLOR_SPACE_SRGB,
                D2D1_BUFFER_PRECISION_8BPC_UNORM,
                D2D1_EXTEND_MODE(mode as i32),
                D2D1_COLOR_INTERPOLATION_MODE_PREMULTIPLIED,
            )?
        };
        let brush = unsafe {
            dc.CreateLinearGradientBrush(
                &D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES {
                    startPoint: start.into().into(),
                    endPoint: end.into().into(),
                },
                None,
                &stops,
            )?
        };
        Ok(Self(brush.cast().unwrap()))
    }
}

impl Brush for LinearGradientBrush {
    #[inline]
    fn handle(&self) -> &ID2D1Brush {
        &self.0
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct RadialGradientBrush(ID2D1Brush);

impl RadialGradientBrush {
    #[inline]
    pub fn new<T, G>(
        ctx: &Context<T>,
        ellipse: impl Into<Ellipse>,
        offset: impl Into<Vector<f32>>,
        mode: GradientMode,
        stops: &[G],
    ) -> Result<Self>
    where
        T: Backend,
        G: Into<GradientStop> + Clone,
    {
        let dc = &ctx.d2d1_device_context;
        let stops: Vec<D2D1_GRADIENT_STOP> = stops
            .iter()
            .cloned()
            .map(|stop| D2D1_GRADIENT_STOP::from(stop.into()))
            .collect();
        let stops = unsafe {
            dc.CreateGradientStopCollection(
                &stops,
                D2D1_COLOR_SPACE_SRGB,
                D2D1_COLOR_SPACE_SRGB,
                D2D1_BUFFER_PRECISION_8BPC_UNORM,
                D2D1_EXTEND_MODE(mode as i32),
                D2D1_COLOR_INTERPOLATION_MODE_PREMULTIPLIED,
            )?
        };
        let ellipse: Ellipse = ellipse.into();
        let offset: Vector<f32> = offset.into();
        let brush = unsafe {
            dc.CreateRadialGradientBrush(
                &D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES {
                    center: ellipse.center.into(),
                    radiusX: ellipse.radius_x,
                    radiusY: ellipse.radius_y,
                    gradientOriginOffset: offset.as_point().into(),
                },
                None,
                &stops,
            )?
        };
        Ok(Self(brush.cast().unwrap()))
    }
}

impl Brush for RadialGradientBrush {
    #[inline]
    fn handle(&self) -> &ID2D1Brush {
        &self.0
    }
}