1use std::{
2 fmt::{Debug, Error, Formatter},
3 ops::Deref,
4 ptr::null,
5 slice,
6};
7
8use core_foundation::{
9 base::{CFTypeID, TCFType},
10 impl_CFTypeDescription, impl_TCFType,
11};
12use libc::{c_void, size_t};
13#[cfg(feature = "objc")]
14use objc2::encode::{Encoding, RefEncode};
15
16use crate::{
17 affine_transform::CGAffineTransform,
18 base::CGFloat,
19 geometry::{CGPoint, CGRect, CGRectZero},
20};
21
22#[repr(C)]
23pub struct __CGPath(c_void);
24
25pub type CGMutablePathRef = *mut __CGPath;
26pub type CGPathRef = *const __CGPath;
27
28#[repr(i32)]
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum CGLineJoin {
31 #[doc(alias = "kCGLineJoinMiter")]
32 Miter,
33 #[doc(alias = "kCGLineJoinRound")]
34 Round,
35 #[doc(alias = "kCGLineJoinBevel")]
36 Bevel,
37}
38
39#[repr(i32)]
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
41pub enum CGLineCap {
42 #[doc(alias = "kCGLineCapButt")]
43 Butt,
44 #[doc(alias = "kCGLineCapRound")]
45 Round,
46 #[doc(alias = "kCGLineCapSquare")]
47 Square,
48}
49
50extern "C" {
51 pub fn CGPathGetTypeID() -> CFTypeID;
52 pub fn CGPathCreateMutable() -> CGMutablePathRef;
53 pub fn CGPathCreateCopy(path: CGPathRef) -> CGPathRef;
54 pub fn CGPathCreateCopyByTransformingPath(path: CGPathRef, transform: *const CGAffineTransform) -> CGPathRef;
55 pub fn CGPathCreateMutableCopy(path: CGPathRef) -> CGMutablePathRef;
56 pub fn CGPathCreateMutableCopyByTransformingPath(path: CGPathRef, transform: *const CGAffineTransform) -> CGMutablePathRef;
57 pub fn CGPathCreateWithRect(rect: CGRect, transform: *const CGAffineTransform) -> CGPathRef;
58 pub fn CGPathCreateWithEllipseInRect(rect: CGRect, transform: *const CGAffineTransform) -> CGPathRef;
59 pub fn CGPathCreateWithRoundedRect(rect: CGRect, cornerWidth: CGFloat, cornerHeight: CGFloat, transform: *const CGAffineTransform) -> CGPathRef;
60 pub fn CGPathAddRoundedRect(
61 path: CGMutablePathRef,
62 transform: *const CGAffineTransform,
63 rect: CGRect,
64 cornerWidth: CGFloat,
65 cornerHeight: CGFloat,
66 );
67 pub fn CGPathCreateCopyByDashingPath(
68 path: CGPathRef,
69 transform: *const CGAffineTransform,
70 phase: CGFloat,
71 lengths: *const CGFloat,
72 count: size_t,
73 ) -> CGPathRef;
74 pub fn CGPathCreateCopyByStrokingPath(
75 path: CGPathRef,
76 transform: *const CGAffineTransform,
77 lineWidth: CGFloat,
78 lineCap: CGLineCap,
79 lineJoin: CGLineJoin,
80 miterLimit: CGFloat,
81 ) -> CGPathRef;
82 pub fn CGPathRetain(path: CGPathRef) -> CGPathRef;
83 pub fn CGPathRelease(path: CGPathRef);
84 pub fn CGPathEqualToPath(path1: CGPathRef, path2: CGPathRef) -> bool;
85 pub fn CGPathMoveToPoint(path: CGMutablePathRef, transform: *const CGAffineTransform, x: CGFloat, y: CGFloat);
86 pub fn CGPathAddLineToPoint(path: CGMutablePathRef, transform: *const CGAffineTransform, x: CGFloat, y: CGFloat);
87 pub fn CGPathAddQuadCurveToPoint(path: CGMutablePathRef, transform: *const CGAffineTransform, cpx: CGFloat, cpy: CGFloat, x: CGFloat, y: CGFloat);
88 pub fn CGPathAddCurveToPoint(
89 path: CGMutablePathRef,
90 transform: *const CGAffineTransform,
91 cp1x: CGFloat,
92 cp1y: CGFloat,
93 cp2x: CGFloat,
94 cp2y: CGFloat,
95 x: CGFloat,
96 y: CGFloat,
97 );
98 pub fn CGPathCloseSubpath(path: CGMutablePathRef);
99 pub fn CGPathAddRect(path: CGMutablePathRef, transform: *const CGAffineTransform, rect: CGRect);
100 pub fn CGPathAddRects(path: CGMutablePathRef, transform: *const CGAffineTransform, rects: *const CGRect, count: size_t);
101 pub fn CGPathAddLines(path: CGMutablePathRef, transform: *const CGAffineTransform, points: *const CGPoint, count: size_t);
102 pub fn CGPathAddEllipseInRect(path: CGMutablePathRef, transform: *const CGAffineTransform, rect: CGRect);
103 pub fn CGPathAddRelativeArc(
104 path: CGMutablePathRef,
105 transform: *const CGAffineTransform,
106 x: CGFloat,
107 y: CGFloat,
108 radius: CGFloat,
109 startAngle: CGFloat,
110 delta: CGFloat,
111 );
112 pub fn CGPathAddArc(
113 path: CGMutablePathRef,
114 transform: *const CGAffineTransform,
115 x: CGFloat,
116 y: CGFloat,
117 radius: CGFloat,
118 startAngle: CGFloat,
119 endAngle: CGFloat,
120 clockwise: bool,
121 );
122 pub fn CGPathAddArcToPoint(
123 path: CGMutablePathRef,
124 transform: *const CGAffineTransform,
125 x1: CGFloat,
126 y1: CGFloat,
127 x2: CGFloat,
128 y2: CGFloat,
129 radius: CGFloat,
130 );
131 pub fn CGPathAddPath(path1: CGMutablePathRef, transform: *const CGAffineTransform, path2: CGPathRef);
132 pub fn CGPathIsEmpty(path: CGPathRef) -> bool;
133 pub fn CGPathIsRect(path: CGPathRef, rect: *mut CGRect) -> bool;
134 pub fn CGPathGetCurrentPoint(path: CGPathRef) -> CGPoint;
135 pub fn CGPathGetBoundingBox(path: CGPathRef) -> CGRect;
136 pub fn CGPathGetPathBoundingBox(path: CGPathRef) -> CGRect;
137 pub fn CGPathContainsPoint(path: CGPathRef, transform: *const CGAffineTransform, point: CGPoint, eoFill: bool) -> bool;
138}
139
140#[repr(i32)]
141#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142pub enum CGPathElementType {
143 #[doc(alias = "kCGPathElementMoveToPoint")]
144 MoveToPoint,
145 #[doc(alias = "kCGPathElementAddLineToPoint")]
146 AddLineToPoint,
147 #[doc(alias = "kCGPathElementAddQuadCurveToPoint")]
148 AddQuadCurveToPoint,
149 #[doc(alias = "kCGPathElementAddCurveToPoint")]
150 AddCurveToPoint,
151 #[doc(alias = "kCGPathElementCloseSubpath")]
152 CloseSubpath,
153}
154
155#[repr(C)]
156pub struct CGPathElement {
157 pub element_type: CGPathElementType,
158 points: *mut CGPoint,
159}
160
161pub type CGPathApplierFunction = extern "C" fn(*mut c_void, *const CGPathElement);
162
163extern "C" {
164 pub fn CGPathApply(path: CGPathRef, info: *mut c_void, function: CGPathApplierFunction);
165 pub fn CGPathCreateCopyByNormalizingPath(path: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
166 pub fn CGPathCreateCopyByUnioningPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
167 pub fn CGPathCreateCopyByIntersectingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
168 pub fn CGPathCreateCopyBySubtractingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
169 pub fn CGPathCreateCopyBySymmetricDifferenceOfPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
170 pub fn CGPathCreateCopyOfLineBySubtractingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
171 pub fn CGPathCreateCopyOfLineByIntersectingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
172 pub fn CGPathCreateSeparateComponents(path: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
173 pub fn CGPathCreateCopyByFlattening(path: CGPathRef, flatteningThreshold: CGFloat) -> CGPathRef;
174 pub fn CGPathIntersectsPath(path1: CGPathRef, path2: CGPathRef, evenOddFillRule: bool) -> bool;
175}
176
177pub struct CGPath(CGPathRef);
178
179impl Drop for CGPath {
180 fn drop(&mut self) {
181 unsafe { CGPathRelease(self.0) }
182 }
183}
184
185impl_TCFType!(CGPath, CGPathRef, CGPathGetTypeID);
186impl_CFTypeDescription!(CGPath);
187
188pub struct CGMutablePath(CGMutablePathRef);
189
190impl Drop for CGMutablePath {
191 fn drop(&mut self) {
192 unsafe { CGPathRelease(self.0) }
193 }
194}
195
196impl_TCFType!(CGMutablePath, CGMutablePathRef, CGPathGetTypeID);
197impl_CFTypeDescription!(CGMutablePath);
198
199impl CGPathElement {
200 pub fn points(&self) -> &[CGPoint] {
201 unsafe {
202 match self.element_type {
203 CGPathElementType::MoveToPoint | CGPathElementType::AddLineToPoint => slice::from_raw_parts(self.points, 1),
204 CGPathElementType::AddQuadCurveToPoint => slice::from_raw_parts(self.points, 2),
205 CGPathElementType::AddCurveToPoint => slice::from_raw_parts(self.points, 3),
206 CGPathElementType::CloseSubpath => &[],
207 }
208 }
209 }
210}
211
212impl Debug for CGPathElement {
213 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
214 write!(formatter, "{:?}: {:?}", self.element_type, self.points())
215 }
216}
217
218pub struct CGPathElementRef {
219 element: *const CGPathElement,
220}
221
222impl CGPathElementRef {
223 fn new(element: *const CGPathElement) -> CGPathElementRef {
224 CGPathElementRef {
225 element,
226 }
227 }
228}
229
230impl Deref for CGPathElementRef {
231 type Target = CGPathElement;
232 fn deref(&self) -> &CGPathElement {
233 unsafe { &*self.element }
234 }
235}
236
237impl CGPath {
238 pub fn new_copy(&self) -> Option<CGPath> {
239 unsafe {
240 let path = CGPathCreateCopy(self.as_concrete_TypeRef());
241 if path.is_null() {
242 None
243 } else {
244 Some(TCFType::wrap_under_create_rule(path))
245 }
246 }
247 }
248
249 pub fn new_copy_by_transforming_path(&self, transform: Option<&CGAffineTransform>) -> Option<CGPath> {
250 unsafe {
251 let path = CGPathCreateCopyByTransformingPath(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform));
252 if path.is_null() {
253 None
254 } else {
255 Some(TCFType::wrap_under_create_rule(path))
256 }
257 }
258 }
259
260 pub fn new_mutable_copy(&self) -> Option<CGMutablePath> {
261 unsafe {
262 let path = CGPathCreateMutableCopy(self.as_concrete_TypeRef());
263 if path.is_null() {
264 None
265 } else {
266 Some(TCFType::wrap_under_create_rule(path))
267 }
268 }
269 }
270
271 pub fn new_mutable_copy_by_transforming_path(&self, transform: Option<&CGAffineTransform>) -> Option<CGMutablePath> {
272 unsafe {
273 let path =
274 CGPathCreateMutableCopyByTransformingPath(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform));
275 if path.is_null() {
276 None
277 } else {
278 Some(TCFType::wrap_under_create_rule(path))
279 }
280 }
281 }
282
283 pub fn from_rect(rect: CGRect, transform: Option<&CGAffineTransform>) -> CGPath {
284 unsafe {
285 let path = CGPathCreateWithRect(rect, transform.map_or(null(), |t| t as *const CGAffineTransform));
286 TCFType::wrap_under_create_rule(path)
287 }
288 }
289
290 pub fn from_ellipse_in_rect(rect: CGRect, transform: Option<&CGAffineTransform>) -> CGPath {
291 unsafe {
292 let path = CGPathCreateWithEllipseInRect(rect, transform.map_or(null(), |t| t as *const CGAffineTransform));
293 TCFType::wrap_under_create_rule(path)
294 }
295 }
296
297 pub fn from_rounded_rect(rect: CGRect, corner_width: CGFloat, corner_height: CGFloat, transform: Option<&CGAffineTransform>) -> CGPath {
298 unsafe {
299 let path = CGPathCreateWithRoundedRect(rect, corner_width, corner_height, transform.map_or(null(), |t| t as *const CGAffineTransform));
300 TCFType::wrap_under_create_rule(path)
301 }
302 }
303
304 pub fn new_copy_by_dashing_path(&self, transform: Option<&CGAffineTransform>, phase: CGFloat, lengths: &[CGFloat]) -> Option<CGPath> {
305 unsafe {
306 let path = CGPathCreateCopyByDashingPath(
307 self.as_concrete_TypeRef(),
308 transform.map_or(null(), |t| t as *const CGAffineTransform),
309 phase,
310 lengths.as_ptr(),
311 lengths.len(),
312 );
313 if path.is_null() {
314 None
315 } else {
316 Some(TCFType::wrap_under_create_rule(path))
317 }
318 }
319 }
320
321 pub fn new_copy_by_stroking_path(
322 &self,
323 transform: Option<&CGAffineTransform>,
324 line_width: CGFloat,
325 line_cap: CGLineCap,
326 line_join: CGLineJoin,
327 miter_limit: CGFloat,
328 ) -> Option<CGPath> {
329 unsafe {
330 let path = CGPathCreateCopyByStrokingPath(
331 self.as_concrete_TypeRef(),
332 transform.map_or(null(), |t| t as *const CGAffineTransform),
333 line_width,
334 line_cap,
335 line_join,
336 miter_limit,
337 );
338 if path.is_null() {
339 None
340 } else {
341 Some(TCFType::wrap_under_create_rule(path))
342 }
343 }
344 }
345
346 pub fn equal(&self, path: &CGPath) -> bool {
347 unsafe { CGPathEqualToPath(self.as_concrete_TypeRef(), path.as_concrete_TypeRef()) }
348 }
349
350 pub fn current_point(&self) -> CGPoint {
351 unsafe { CGPathGetCurrentPoint(self.as_concrete_TypeRef()) }
352 }
353
354 pub fn bounding_box(&self) -> CGRect {
355 unsafe { CGPathGetBoundingBox(self.as_concrete_TypeRef()) }
356 }
357
358 pub fn path_bounding_box(&self) -> CGRect {
359 unsafe { CGPathGetPathBoundingBox(self.as_concrete_TypeRef()) }
360 }
361
362 pub fn is_empty(&self) -> bool {
363 unsafe { CGPathIsEmpty(self.as_concrete_TypeRef()) }
364 }
365
366 pub fn is_rect(&self) -> Option<CGRect> {
367 let mut rect = CGRectZero;
368 let result = unsafe { CGPathIsRect(self.as_concrete_TypeRef(), &mut rect) };
369 if result {
370 Some(rect)
371 } else {
372 None
373 }
374 }
375
376 pub fn contains_point(&self, transform: Option<&CGAffineTransform>, point: CGPoint, eo_fill: bool) -> bool {
377 unsafe { CGPathContainsPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), point, eo_fill) }
378 }
379
380 pub fn apply<F>(&self, closure: F)
381 where
382 F: FnMut(CGPathElementRef),
383 {
384 unsafe {
385 CGPathApply(self.as_concrete_TypeRef(), &closure as *const _ as *mut c_void, callback::<F>);
386 }
387
388 extern "C" fn callback<F>(info: *mut c_void, element: *const CGPathElement)
389 where
390 F: FnMut(CGPathElementRef),
391 {
392 unsafe {
393 let closure = info as *mut F;
394 (*closure)(CGPathElementRef::new(element));
395 }
396 }
397 }
398}
399
400impl CGMutablePath {
401 pub fn new() -> CGMutablePath {
402 unsafe { TCFType::wrap_under_create_rule(CGPathCreateMutable()) }
403 }
404
405 pub fn add_rounded_rect(&self, transform: Option<&CGAffineTransform>, rect: CGRect, corner_width: CGFloat, corner_height: CGFloat) {
406 unsafe {
407 CGPathAddRoundedRect(
408 self.as_concrete_TypeRef(),
409 transform.map_or(null(), |t| t as *const CGAffineTransform),
410 rect,
411 corner_width,
412 corner_height,
413 )
414 }
415 }
416
417 pub fn add_line_to_point(&self, transform: Option<&CGAffineTransform>, x: CGFloat, y: CGFloat) {
418 unsafe { CGPathAddLineToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), x, y) }
419 }
420
421 pub fn add_quad_curve_to_point(&self, transform: Option<&CGAffineTransform>, cpx: CGFloat, cpy: CGFloat, x: CGFloat, y: CGFloat) {
422 unsafe { CGPathAddQuadCurveToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), cpx, cpy, x, y) }
423 }
424
425 pub fn add_curve_to_point(
426 &self,
427 transform: Option<&CGAffineTransform>,
428 cp1x: CGFloat,
429 cp1y: CGFloat,
430 cp2x: CGFloat,
431 cp2y: CGFloat,
432 x: CGFloat,
433 y: CGFloat,
434 ) {
435 unsafe {
436 CGPathAddCurveToPoint(
437 self.as_concrete_TypeRef(),
438 transform.map_or(null(), |t| t as *const CGAffineTransform),
439 cp1x,
440 cp1y,
441 cp2x,
442 cp2y,
443 x,
444 y,
445 )
446 }
447 }
448
449 pub fn add_rect(&self, transform: Option<&CGAffineTransform>, rect: CGRect) {
450 unsafe { CGPathAddRect(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), rect) }
451 }
452
453 pub fn add_rects(&self, transform: Option<&CGAffineTransform>, rects: &[CGRect]) {
454 unsafe {
455 CGPathAddRects(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), rects.as_ptr(), rects.len())
456 }
457 }
458
459 pub fn add_lines(&self, transform: Option<&CGAffineTransform>, points: &[CGPoint]) {
460 unsafe {
461 CGPathAddLines(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), points.as_ptr(), points.len())
462 }
463 }
464
465 pub fn add_ellipse_in_rect(&self, transform: Option<&CGAffineTransform>, rect: CGRect) {
466 unsafe { CGPathAddEllipseInRect(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), rect) }
467 }
468
469 pub fn add_relative_arc(
470 &self,
471 transform: Option<&CGAffineTransform>,
472 x: CGFloat,
473 y: CGFloat,
474 radius: CGFloat,
475 start_angle: CGFloat,
476 delta: CGFloat,
477 ) {
478 unsafe {
479 CGPathAddRelativeArc(
480 self.as_concrete_TypeRef(),
481 transform.map_or(null(), |t| t as *const CGAffineTransform),
482 x,
483 y,
484 radius,
485 start_angle,
486 delta,
487 )
488 }
489 }
490
491 pub fn add_arc(
492 &self,
493 transform: Option<&CGAffineTransform>,
494 x: CGFloat,
495 y: CGFloat,
496 radius: CGFloat,
497 start_angle: CGFloat,
498 end_angle: CGFloat,
499 clockwise: bool,
500 ) {
501 unsafe {
502 CGPathAddArc(
503 self.as_concrete_TypeRef(),
504 transform.map_or(null(), |t| t as *const CGAffineTransform),
505 x,
506 y,
507 radius,
508 start_angle,
509 end_angle,
510 clockwise,
511 )
512 }
513 }
514
515 pub fn add_arc_to_point(&self, transform: Option<&CGAffineTransform>, x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat, radius: CGFloat) {
516 unsafe {
517 CGPathAddArcToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), x1, y1, x2, y2, radius)
518 }
519 }
520
521 pub fn add_path(&self, transform: Option<&CGAffineTransform>, path: &CGPath) {
522 unsafe { CGPathAddPath(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), path.as_concrete_TypeRef()) }
523 }
524
525 pub fn close_subpath(&self) {
526 unsafe { CGPathCloseSubpath(self.as_concrete_TypeRef()) }
527 }
528
529 pub fn move_to_point(&self, transform: Option<&CGAffineTransform>, x: CGFloat, y: CGFloat) {
530 unsafe { CGPathMoveToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), x, y) }
531 }
532
533 pub fn to_immutable(&self) -> CGPath {
534 unsafe { CGPath::wrap_under_get_rule(self.as_concrete_TypeRef()) }
535 }
536}
537
538#[cfg(feature = "objc")]
539unsafe impl RefEncode for __CGPath {
540 const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("CGPath", &[]));
541}