x_graphics/direct2d/
path.rs

1use std::cell::Cell;
2
3use windows::Win32::Graphics::Direct2D::{
4    Common::{
5        D2D1_BEZIER_SEGMENT, D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_BEGIN_HOLLOW, D2D1_FIGURE_END_CLOSED, D2D1_FIGURE_END_OPEN,
6        D2D1_FILL_MODE_ALTERNATE, D2D1_FILL_MODE_WINDING, D2D_POINT_2F, D2D_SIZE_F,
7    },
8    ID2D1GeometrySink, ID2D1PathGeometry, D2D1_ARC_SEGMENT, D2D1_ARC_SIZE_LARGE, D2D1_ARC_SIZE_SMALL, D2D1_QUADRATIC_BEZIER_SEGMENT,
9    D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE,
10};
11
12use super::device::D2DDeviceContext;
13use crate::{
14    error::GraphicsError,
15    geometry::FRect,
16    path::{FillType, PathBackend},
17    Float,
18};
19
20#[derive(Clone, Debug)]
21pub struct D2DPath {
22    path: ID2D1PathGeometry,
23    sink: ID2D1GeometrySink,
24    closed: Cell<bool>,
25    fill_type: FillType,
26}
27
28impl PathBackend for D2DPath {
29    type DeviceContextType = D2DDeviceContext;
30
31    fn new(context: Option<&Self::DeviceContextType>) -> Result<Self, GraphicsError> {
32        let context = context.ok_or(none_param_error!(context))?;
33        let path = unsafe { context.factory.CreatePathGeometry().map_err(|err| GraphicsError::CreationFailed(err.to_string()))? };
34        let sink = unsafe { path.Open().map_err(|err| GraphicsError::CreationFailed(err.to_string()))? };
35        unsafe { sink.SetFillMode(D2D1_FILL_MODE_WINDING) };
36        Ok(Self {
37            path,
38            sink,
39            closed: Cell::new(false),
40            fill_type: FillType::Winding,
41        })
42    }
43
44    fn get_fill_type(&self) -> FillType {
45        self.fill_type
46    }
47
48    fn set_fill_type(&mut self, fill_type: FillType) {
49        let fill_mode = match fill_type {
50            FillType::Winding => D2D1_FILL_MODE_WINDING,
51            FillType::EvenOdd => D2D1_FILL_MODE_ALTERNATE,
52        };
53        unsafe { self.sink().SetFillMode(fill_mode) };
54        self.fill_type = fill_type;
55    }
56
57    fn begin(&mut self) {
58        if !self.closed.get() {
59            unsafe { self.sink.Close().ok() };
60            self.closed.set(true);
61        }
62        if let Ok(sink) = unsafe { self.path.Open() } {
63            self.sink = sink;
64        }
65    }
66
67    fn close(&mut self) {
68        unsafe {
69            self.sink().EndFigure(D2D1_FIGURE_END_CLOSED);
70        }
71    }
72
73    fn move_to(&mut self, x: Float, y: Float) {
74        unsafe {
75            let point = D2D_POINT_2F {
76                x,
77                y,
78            };
79            self.sink().BeginFigure(point, D2D1_FIGURE_BEGIN_FILLED);
80        }
81    }
82
83    fn line_to(&mut self, x: Float, y: Float) {
84        unsafe {
85            let point = D2D_POINT_2F {
86                x,
87                y,
88            };
89            self.sink().AddLine(point);
90        }
91    }
92
93    fn arc_to(&mut self, x1: Float, y1: Float, x2: Float, y2: Float, radius: Float) {
94        let center_x = x1 + (x2 - x1) / 2.0;
95        let center_y = y1 + (y2 - y1) / 2.0;
96        let start_angle = (y1 - center_y).atan2(x1 - center_x);
97        let end_angle = (y2 - center_y).atan2(x2 - center_x);
98        let arc_segment = D2D1_ARC_SEGMENT {
99            point: D2D_POINT_2F {
100                x: x2,
101                y: y2,
102            },
103            size: D2D_SIZE_F {
104                width: radius,
105                height: radius,
106            },
107            rotationAngle: 0.0,
108            sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
109            arcSize: if (end_angle - start_angle).abs() < 180.0 {
110                D2D1_ARC_SIZE_SMALL
111            } else {
112                D2D1_ARC_SIZE_LARGE
113            },
114        };
115        unsafe {
116            self.sink().AddArc(&arc_segment);
117        }
118    }
119
120    fn bezier_curve_to(&mut self, cpx1: Float, cpy1: Float, cpx2: Float, cpy2: Float, x: Float, y: Float) {
121        let bezier_segment = D2D1_BEZIER_SEGMENT {
122            point1: D2D_POINT_2F {
123                x: cpx1,
124                y: cpy1,
125            },
126            point2: D2D_POINT_2F {
127                x: cpx2,
128                y: cpy2,
129            },
130            point3: D2D_POINT_2F {
131                x,
132                y,
133            },
134        };
135
136        unsafe {
137            self.sink().AddBezier(&bezier_segment);
138        }
139    }
140
141    fn quad_curve_to(&mut self, cpx: Float, cpy: Float, x: Float, y: Float) {
142        let quad_segment = D2D1_QUADRATIC_BEZIER_SEGMENT {
143            point1: D2D_POINT_2F {
144                x: cpx,
145                y: cpy,
146            },
147            point2: D2D_POINT_2F {
148                x,
149                y,
150            },
151        };
152
153        unsafe {
154            self.sink().AddQuadraticBezier(&quad_segment);
155        }
156    }
157
158    fn add_arc(&mut self, x: Float, y: Float, radius: Float, start_angle: Float, end_angle: Float, clockwise: bool) {
159        let start_x = x + radius * start_angle.cos();
160        let start_y = y + radius * start_angle.sin();
161        let end_x = x + radius * end_angle.cos();
162        let end_y = y + radius * end_angle.sin();
163        let arc_segment = D2D1_ARC_SEGMENT {
164            point: D2D_POINT_2F {
165                x: end_x,
166                y: end_y,
167            },
168            size: D2D_SIZE_F {
169                width: radius,
170                height: radius,
171            },
172            rotationAngle: 0.0,
173            sweepDirection: if clockwise {
174                D2D1_SWEEP_DIRECTION_CLOCKWISE
175            } else {
176                D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE
177            },
178            arcSize: if (end_angle - start_angle).abs() < std::f32::consts::PI {
179                D2D1_ARC_SIZE_SMALL
180            } else {
181                D2D1_ARC_SIZE_LARGE
182            },
183        };
184        let sink = self.sink();
185        unsafe {
186            sink.BeginFigure(
187                D2D_POINT_2F {
188                    x: start_x,
189                    y: start_y,
190                },
191                D2D1_FIGURE_BEGIN_HOLLOW,
192            );
193            sink.AddArc(&arc_segment);
194            sink.EndFigure(D2D1_FIGURE_END_OPEN);
195        }
196    }
197
198    fn add_rect(&mut self, x: Float, y: Float, width: Float, height: Float) {
199        let sink = self.sink();
200        unsafe {
201            sink.BeginFigure(
202                D2D_POINT_2F {
203                    x,
204                    y,
205                },
206                D2D1_FIGURE_BEGIN_FILLED,
207            );
208            sink.AddLine(D2D_POINT_2F {
209                x: x + width,
210                y,
211            });
212            sink.AddLine(D2D_POINT_2F {
213                x: x + width,
214                y: y + height,
215            });
216            sink.AddLine(D2D_POINT_2F {
217                x,
218                y: y + height,
219            });
220            sink.EndFigure(D2D1_FIGURE_END_OPEN);
221        }
222    }
223
224    fn add_circle(&mut self, x: Float, y: Float, radius: Float) {
225        let mut arc_segment = D2D1_ARC_SEGMENT {
226            point: D2D_POINT_2F {
227                x: x + radius,
228                y,
229            },
230            size: D2D_SIZE_F {
231                width: radius,
232                height: radius,
233            },
234            rotationAngle: 0.0,
235            sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
236            arcSize: D2D1_ARC_SIZE_LARGE,
237        };
238        let sink = self.sink();
239        unsafe {
240            sink.BeginFigure(
241                D2D_POINT_2F {
242                    x: x - radius,
243                    y,
244                },
245                D2D1_FIGURE_BEGIN_FILLED,
246            );
247            sink.AddArc(&arc_segment);
248            arc_segment.point = D2D_POINT_2F {
249                x: x - radius,
250                y,
251            };
252            sink.AddArc(&arc_segment);
253            sink.EndFigure(D2D1_FIGURE_END_OPEN);
254        }
255    }
256
257    fn add_ellipse(&mut self, x: Float, y: Float, width: Float, height: Float) {
258        let start_x = x + width;
259        let start_y = y + height / 2.0;
260        let mut arc_segment = D2D1_ARC_SEGMENT {
261            point: D2D_POINT_2F {
262                x,
263                y: start_y,
264            },
265            size: D2D_SIZE_F {
266                width: width / 2.0,
267                height: height / 2.0,
268            },
269            rotationAngle: 0.0,
270            sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
271            arcSize: D2D1_ARC_SIZE_LARGE,
272        };
273        let sink = self.sink();
274        unsafe {
275            sink.BeginFigure(
276                D2D_POINT_2F {
277                    x: start_x,
278                    y: start_y,
279                },
280                D2D1_FIGURE_BEGIN_FILLED,
281            );
282            sink.AddArc(&arc_segment);
283            arc_segment.point = D2D_POINT_2F {
284                x: start_x,
285                y: start_y,
286            };
287            sink.AddArc(&arc_segment);
288            sink.EndFigure(D2D1_FIGURE_END_OPEN);
289        }
290    }
291
292    fn add_rounded_rect(&mut self, x: Float, y: Float, width: Float, height: Float, radius: Float) {
293        let sink = self.sink();
294        unsafe {
295            sink.BeginFigure(
296                D2D_POINT_2F {
297                    x: x + radius,
298                    y,
299                },
300                D2D1_FIGURE_BEGIN_FILLED,
301            );
302            sink.AddLine(D2D_POINT_2F {
303                x: x + width - radius,
304                y,
305            });
306            Self::add_corner_arc(sink, x + width, y + radius, radius);
307            sink.AddLine(D2D_POINT_2F {
308                x: x + width,
309                y: y + height - radius,
310            });
311            Self::add_corner_arc(sink, x + width - radius, y + height, radius);
312            sink.AddLine(D2D_POINT_2F {
313                x: x + radius,
314                y: y + height,
315            });
316            Self::add_corner_arc(sink, x, y + height - radius, radius);
317            sink.AddLine(D2D_POINT_2F {
318                x,
319                y: y + radius,
320            });
321            Self::add_corner_arc(sink, x + radius, y, radius);
322            sink.EndFigure(D2D1_FIGURE_END_OPEN);
323        }
324    }
325
326    fn bounds(&self) -> FRect {
327        let rect = unsafe { self.path().GetBounds(None).unwrap_or_default() };
328        rect.into()
329    }
330
331    fn is_empty(&self) -> bool {
332        unsafe { self.path().GetSegmentCount() }.unwrap_or(0) == 0
333    }
334}
335
336impl D2DPath {
337    pub(super) fn path(&self) -> &ID2D1PathGeometry {
338        if !self.closed.get() {
339            unsafe {
340                self.sink.Close().ok();
341            }
342            self.closed.set(true);
343        }
344        &self.path
345    }
346
347    fn add_corner_arc(sink: &ID2D1GeometrySink, x: Float, y: Float, radius: Float) {
348        let arc_segment = D2D1_ARC_SEGMENT {
349            point: D2D_POINT_2F {
350                x,
351                y,
352            },
353            size: D2D_SIZE_F {
354                width: radius,
355                height: radius,
356            },
357            rotationAngle: 0.0,
358            sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
359            arcSize: D2D1_ARC_SIZE_SMALL,
360        };
361        unsafe {
362            sink.AddArc(&arc_segment);
363        }
364    }
365
366    fn sink(&mut self) -> &ID2D1GeometrySink {
367        if self.closed.get() {
368            if let Ok(sink) = unsafe { self.path.Open() } {
369                self.sink = sink;
370                unsafe {
371                    self.path.Stream(&self.sink).ok();
372                }
373            }
374        }
375        &self.sink
376    }
377}
378
379impl Drop for D2DPath {
380    fn drop(&mut self) {
381        unsafe {
382            if !self.closed.get() {
383                self.sink.Close().ok();
384            }
385        }
386    }
387}