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}