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