use windows::{
core::Interface,
Foundation::Numerics::Matrix3x2,
Win32::Graphics::Direct2D::{
Common::D2D_POINT_2F, ID2D1Brush, ID2D1RenderTarget, D2D1_BRUSH_PROPERTIES, D2D1_EXTEND_MODE_CLAMP, D2D1_GAMMA_2_2,
D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES, D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES,
},
};
use super::gradient::D2DGradient;
use crate::{
brush::BrushBackend,
gradient::GradientData,
paint::{Color, BLACK},
Float,
};
#[derive(Clone, Debug)]
enum BrushData {
Solid(Color),
Gradient(D2DGradient),
}
impl Default for BrushData {
fn default() -> Self {
Self::Solid(BLACK)
}
}
#[derive(Clone, Debug, Default)]
pub struct D2DBrush {
data: BrushData,
brush: Option<ID2D1Brush>,
render_target_identity: Option<usize>,
}
impl BrushBackend for D2DBrush {
type GradientType = D2DGradient;
fn new_solid(color: Color) -> Self {
Self {
data: BrushData::Solid(color),
brush: None,
render_target_identity: None,
}
}
fn new_gradient(gradient: Self::GradientType) -> Self {
Self {
data: BrushData::Gradient(gradient),
brush: None,
render_target_identity: None,
}
}
}
impl D2DBrush {
pub(super) fn d2d_brush(&mut self, render_target: &ID2D1RenderTarget, opacity: Float) -> Option<ID2D1Brush> {
if self.render_target_identity != Some(render_target.as_raw() as usize) {
self.brush = None;
}
self.brush
.as_ref()
.map(|brush| {
unsafe {
brush.SetOpacity(opacity);
}
brush.clone()
})
.or_else(|| {
let brush_properties = D2D1_BRUSH_PROPERTIES {
opacity,
transform: Matrix3x2::identity(),
};
let brush: Option<ID2D1Brush> = match &self.data {
BrushData::Solid(color) => {
let brush = unsafe { render_target.CreateSolidColorBrush(&(*color).into(), Some(&brush_properties)).ok() };
brush.map(|brush| brush.into())
}
BrushData::Gradient(gradient) => {
let color_stops = unsafe {
render_target.CreateGradientStopCollection(gradient.color_stops(), D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP).ok().unwrap()
};
match gradient.data() {
GradientData::Linear((start, end)) => unsafe {
render_target
.CreateLinearGradientBrush(
&D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES {
startPoint: (*start).into(),
endPoint: (*end).into(),
},
Some(&brush_properties),
&color_stops,
)
.ok()
.map(|brush| brush.into())
},
GradientData::Radial((start, end, radius)) => unsafe {
render_target
.CreateRadialGradientBrush(
&D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES {
center: (*start).into(),
gradientOriginOffset: D2D_POINT_2F {
x: end.x - start.x,
y: end.y - start.y,
},
radiusX: *radius,
radiusY: *radius,
},
Some(&brush_properties),
&color_stops,
)
.ok()
.map(|brush| brush.into())
},
}
}
};
self.brush = brush.clone();
self.render_target_identity = Some(render_target.as_raw() as usize);
brush
})
}
pub(super) fn set_opacity(&mut self, opacity: Float) {
if let Some(brush) = self.brush.as_ref() {
unsafe {
brush.SetOpacity(opacity);
}
}
}
}