core_graphics2/
path.rs

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}