use std::cell::Cell;
use windows::Win32::Graphics::Direct2D::{
Common::{
D2D1_BEZIER_SEGMENT, D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_BEGIN_HOLLOW, D2D1_FIGURE_END_CLOSED, D2D1_FIGURE_END_OPEN,
D2D1_FILL_MODE_ALTERNATE, D2D1_FILL_MODE_WINDING, D2D_POINT_2F, D2D_SIZE_F,
},
ID2D1GeometrySink, ID2D1PathGeometry, D2D1_ARC_SEGMENT, D2D1_ARC_SIZE_LARGE, D2D1_ARC_SIZE_SMALL, D2D1_QUADRATIC_BEZIER_SEGMENT,
D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE,
};
use super::device::D2DDeviceContext;
use crate::{
error::GraphicsError,
geometry::FRect,
path::{FillType, PathBackend},
Float,
};
#[derive(Clone, Debug)]
pub struct D2DPath {
path: ID2D1PathGeometry,
sink: ID2D1GeometrySink,
closed: Cell<bool>,
fill_type: FillType,
}
impl PathBackend for D2DPath {
type DeviceContextType = D2DDeviceContext;
fn new(context: Option<&Self::DeviceContextType>) -> Result<Self, GraphicsError> {
let context = context.ok_or(none_param_error!(context))?;
let path = unsafe { context.factory.CreatePathGeometry().map_err(|err| GraphicsError::CreationFailed(err.to_string()))? };
let sink = unsafe { path.Open().map_err(|err| GraphicsError::CreationFailed(err.to_string()))? };
unsafe { sink.SetFillMode(D2D1_FILL_MODE_WINDING) };
Ok(Self {
path,
sink,
closed: Cell::new(false),
fill_type: FillType::Winding,
})
}
fn get_fill_type(&self) -> FillType {
self.fill_type
}
fn set_fill_type(&mut self, fill_type: FillType) {
let fill_mode = match fill_type {
FillType::Winding => D2D1_FILL_MODE_WINDING,
FillType::EvenOdd => D2D1_FILL_MODE_ALTERNATE,
};
unsafe { self.sink().SetFillMode(fill_mode) };
self.fill_type = fill_type;
}
fn begin(&mut self) {
if !self.closed.get() {
unsafe { self.sink.Close().ok() };
self.closed.set(true);
}
if let Ok(sink) = unsafe { self.path.Open() } {
self.sink = sink;
}
}
fn close(&mut self) {
unsafe {
self.sink().EndFigure(D2D1_FIGURE_END_CLOSED);
}
}
fn move_to(&mut self, x: Float, y: Float) {
unsafe {
let point = D2D_POINT_2F {
x,
y,
};
self.sink().BeginFigure(point, D2D1_FIGURE_BEGIN_FILLED);
}
}
fn line_to(&mut self, x: Float, y: Float) {
unsafe {
let point = D2D_POINT_2F {
x,
y,
};
self.sink().AddLine(point);
}
}
fn arc_to(&mut self, x1: Float, y1: Float, x2: Float, y2: Float, radius: Float) {
let center_x = x1 + (x2 - x1) / 2.0;
let center_y = y1 + (y2 - y1) / 2.0;
let start_angle = (y1 - center_y).atan2(x1 - center_x);
let end_angle = (y2 - center_y).atan2(x2 - center_x);
let arc_segment = D2D1_ARC_SEGMENT {
point: D2D_POINT_2F {
x: x2,
y: y2,
},
size: D2D_SIZE_F {
width: radius,
height: radius,
},
rotationAngle: 0.0,
sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
arcSize: if (end_angle - start_angle).abs() < 180.0 {
D2D1_ARC_SIZE_SMALL
} else {
D2D1_ARC_SIZE_LARGE
},
};
unsafe {
self.sink().AddArc(&arc_segment);
}
}
fn bezier_curve_to(&mut self, cpx1: Float, cpy1: Float, cpx2: Float, cpy2: Float, x: Float, y: Float) {
let bezier_segment = D2D1_BEZIER_SEGMENT {
point1: D2D_POINT_2F {
x: cpx1,
y: cpy1,
},
point2: D2D_POINT_2F {
x: cpx2,
y: cpy2,
},
point3: D2D_POINT_2F {
x,
y,
},
};
unsafe {
self.sink().AddBezier(&bezier_segment);
}
}
fn quad_curve_to(&mut self, cpx: Float, cpy: Float, x: Float, y: Float) {
let quad_segment = D2D1_QUADRATIC_BEZIER_SEGMENT {
point1: D2D_POINT_2F {
x: cpx,
y: cpy,
},
point2: D2D_POINT_2F {
x,
y,
},
};
unsafe {
self.sink().AddQuadraticBezier(&quad_segment);
}
}
fn add_arc(&mut self, x: Float, y: Float, radius: Float, start_angle: Float, end_angle: Float, clockwise: bool) {
let start_x = x + radius * start_angle.cos();
let start_y = y + radius * start_angle.sin();
let end_x = x + radius * end_angle.cos();
let end_y = y + radius * end_angle.sin();
let arc_segment = D2D1_ARC_SEGMENT {
point: D2D_POINT_2F {
x: end_x,
y: end_y,
},
size: D2D_SIZE_F {
width: radius,
height: radius,
},
rotationAngle: 0.0,
sweepDirection: if clockwise {
D2D1_SWEEP_DIRECTION_CLOCKWISE
} else {
D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE
},
arcSize: if (end_angle - start_angle).abs() < std::f32::consts::PI {
D2D1_ARC_SIZE_SMALL
} else {
D2D1_ARC_SIZE_LARGE
},
};
let sink = self.sink();
unsafe {
sink.BeginFigure(
D2D_POINT_2F {
x: start_x,
y: start_y,
},
D2D1_FIGURE_BEGIN_HOLLOW,
);
sink.AddArc(&arc_segment);
sink.EndFigure(D2D1_FIGURE_END_OPEN);
}
}
fn add_rect(&mut self, x: Float, y: Float, width: Float, height: Float) {
let sink = self.sink();
unsafe {
sink.BeginFigure(
D2D_POINT_2F {
x,
y,
},
D2D1_FIGURE_BEGIN_FILLED,
);
sink.AddLine(D2D_POINT_2F {
x: x + width,
y,
});
sink.AddLine(D2D_POINT_2F {
x: x + width,
y: y + height,
});
sink.AddLine(D2D_POINT_2F {
x,
y: y + height,
});
sink.EndFigure(D2D1_FIGURE_END_OPEN);
}
}
fn add_circle(&mut self, x: Float, y: Float, radius: Float) {
let mut arc_segment = D2D1_ARC_SEGMENT {
point: D2D_POINT_2F {
x: x + radius,
y,
},
size: D2D_SIZE_F {
width: radius,
height: radius,
},
rotationAngle: 0.0,
sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
arcSize: D2D1_ARC_SIZE_LARGE,
};
let sink = self.sink();
unsafe {
sink.BeginFigure(
D2D_POINT_2F {
x: x - radius,
y,
},
D2D1_FIGURE_BEGIN_FILLED,
);
sink.AddArc(&arc_segment);
arc_segment.point = D2D_POINT_2F {
x: x - radius,
y,
};
sink.AddArc(&arc_segment);
sink.EndFigure(D2D1_FIGURE_END_OPEN);
}
}
fn add_ellipse(&mut self, x: Float, y: Float, width: Float, height: Float) {
let start_x = x + width;
let start_y = y + height / 2.0;
let mut arc_segment = D2D1_ARC_SEGMENT {
point: D2D_POINT_2F {
x,
y: start_y,
},
size: D2D_SIZE_F {
width: width / 2.0,
height: height / 2.0,
},
rotationAngle: 0.0,
sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
arcSize: D2D1_ARC_SIZE_LARGE,
};
let sink = self.sink();
unsafe {
sink.BeginFigure(
D2D_POINT_2F {
x: start_x,
y: start_y,
},
D2D1_FIGURE_BEGIN_FILLED,
);
sink.AddArc(&arc_segment);
arc_segment.point = D2D_POINT_2F {
x: start_x,
y: start_y,
};
sink.AddArc(&arc_segment);
sink.EndFigure(D2D1_FIGURE_END_OPEN);
}
}
fn add_rounded_rect(&mut self, x: Float, y: Float, width: Float, height: Float, radius: Float) {
let sink = self.sink();
unsafe {
sink.BeginFigure(
D2D_POINT_2F {
x: x + radius,
y,
},
D2D1_FIGURE_BEGIN_FILLED,
);
sink.AddLine(D2D_POINT_2F {
x: x + width - radius,
y,
});
Self::add_corner_arc(sink, x + width, y + radius, radius);
sink.AddLine(D2D_POINT_2F {
x: x + width,
y: y + height - radius,
});
Self::add_corner_arc(sink, x + width - radius, y + height, radius);
sink.AddLine(D2D_POINT_2F {
x: x + radius,
y: y + height,
});
Self::add_corner_arc(sink, x, y + height - radius, radius);
sink.AddLine(D2D_POINT_2F {
x,
y: y + radius,
});
Self::add_corner_arc(sink, x + radius, y, radius);
sink.EndFigure(D2D1_FIGURE_END_OPEN);
}
}
fn bounds(&self) -> FRect {
let rect = unsafe { self.path().GetBounds(None).unwrap_or_default() };
rect.into()
}
fn is_empty(&self) -> bool {
unsafe { self.path().GetSegmentCount() }.unwrap_or(0) == 0
}
}
impl D2DPath {
pub(super) fn path(&self) -> &ID2D1PathGeometry {
if !self.closed.get() {
unsafe {
self.sink.Close().ok();
}
self.closed.set(true);
}
&self.path
}
fn add_corner_arc(sink: &ID2D1GeometrySink, x: Float, y: Float, radius: Float) {
let arc_segment = D2D1_ARC_SEGMENT {
point: D2D_POINT_2F {
x,
y,
},
size: D2D_SIZE_F {
width: radius,
height: radius,
},
rotationAngle: 0.0,
sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
arcSize: D2D1_ARC_SIZE_SMALL,
};
unsafe {
sink.AddArc(&arc_segment);
}
}
fn sink(&mut self) -> &ID2D1GeometrySink {
if self.closed.get() {
if let Ok(sink) = unsafe { self.path.Open() } {
self.sink = sink;
unsafe {
self.path.Stream(&self.sink).ok();
}
}
}
&self.sink
}
}
impl Drop for D2DPath {
fn drop(&mut self) {
unsafe {
if !self.closed.get() {
self.sink.Close().ok();
}
}
}
}