Skip to main content

core_graphics2/
path.rs

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}