use std::{
fmt::{Debug, Error, Formatter},
ops::Deref,
ptr::null,
slice,
};
use core_foundation::base::{CFTypeID, TCFType};
use libc::{c_void, size_t};
#[cfg(feature = "objc")]
use objc2::encode::{Encoding, RefEncode};
use crate::{
affine_transform::CGAffineTransform,
base::CGFloat,
geometry::{CGPoint, CGRect, CGRectZero},
};
#[repr(C)]
pub struct __CGPath(c_void);
pub type CGMutablePathRef = *mut __CGPath;
pub type CGPathRef = *const __CGPath;
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CGLineJoin {
#[doc(alias = "kCGLineJoinMiter")]
Miter,
#[doc(alias = "kCGLineJoinRound")]
Round,
#[doc(alias = "kCGLineJoinBevel")]
Bevel,
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CGLineCap {
#[doc(alias = "kCGLineCapButt")]
Butt,
#[doc(alias = "kCGLineCapRound")]
Round,
#[doc(alias = "kCGLineCapSquare")]
Square,
}
extern "C" {
pub fn CGPathGetTypeID() -> CFTypeID;
pub fn CGPathCreateMutable() -> CGMutablePathRef;
pub fn CGPathCreateCopy(path: CGPathRef) -> CGPathRef;
pub fn CGPathCreateCopyByTransformingPath(path: CGPathRef, transform: *const CGAffineTransform) -> CGPathRef;
pub fn CGPathCreateMutableCopy(path: CGPathRef) -> CGMutablePathRef;
pub fn CGPathCreateMutableCopyByTransformingPath(path: CGPathRef, transform: *const CGAffineTransform) -> CGMutablePathRef;
pub fn CGPathCreateWithRect(rect: CGRect, transform: *const CGAffineTransform) -> CGPathRef;
pub fn CGPathCreateWithEllipseInRect(rect: CGRect, transform: *const CGAffineTransform) -> CGPathRef;
pub fn CGPathCreateWithRoundedRect(rect: CGRect, cornerWidth: CGFloat, cornerHeight: CGFloat, transform: *const CGAffineTransform) -> CGPathRef;
pub fn CGPathAddRoundedRect(
path: CGMutablePathRef,
transform: *const CGAffineTransform,
rect: CGRect,
cornerWidth: CGFloat,
cornerHeight: CGFloat,
);
pub fn CGPathCreateCopyByDashingPath(
path: CGPathRef,
transform: *const CGAffineTransform,
phase: CGFloat,
lengths: *const CGFloat,
count: size_t,
) -> CGPathRef;
pub fn CGPathCreateCopyByStrokingPath(
path: CGPathRef,
transform: *const CGAffineTransform,
lineWidth: CGFloat,
lineCap: CGLineCap,
lineJoin: CGLineJoin,
miterLimit: CGFloat,
) -> CGPathRef;
pub fn CGPathRetain(path: CGPathRef) -> CGPathRef;
pub fn CGPathRelease(path: CGPathRef);
pub fn CGPathEqualToPath(path1: CGPathRef, path2: CGPathRef) -> bool;
pub fn CGPathMoveToPoint(path: CGMutablePathRef, transform: *const CGAffineTransform, x: CGFloat, y: CGFloat);
pub fn CGPathAddLineToPoint(path: CGMutablePathRef, transform: *const CGAffineTransform, x: CGFloat, y: CGFloat);
pub fn CGPathAddQuadCurveToPoint(path: CGMutablePathRef, transform: *const CGAffineTransform, cpx: CGFloat, cpy: CGFloat, x: CGFloat, y: CGFloat);
pub fn CGPathAddCurveToPoint(
path: CGMutablePathRef,
transform: *const CGAffineTransform,
cp1x: CGFloat,
cp1y: CGFloat,
cp2x: CGFloat,
cp2y: CGFloat,
x: CGFloat,
y: CGFloat,
);
pub fn CGPathCloseSubpath(path: CGMutablePathRef);
pub fn CGPathAddRect(path: CGMutablePathRef, transform: *const CGAffineTransform, rect: CGRect);
pub fn CGPathAddRects(path: CGMutablePathRef, transform: *const CGAffineTransform, rects: *const CGRect, count: size_t);
pub fn CGPathAddLines(path: CGMutablePathRef, transform: *const CGAffineTransform, points: *const CGPoint, count: size_t);
pub fn CGPathAddEllipseInRect(path: CGMutablePathRef, transform: *const CGAffineTransform, rect: CGRect);
pub fn CGPathAddRelativeArc(
path: CGMutablePathRef,
transform: *const CGAffineTransform,
x: CGFloat,
y: CGFloat,
radius: CGFloat,
startAngle: CGFloat,
delta: CGFloat,
);
pub fn CGPathAddArc(
path: CGMutablePathRef,
transform: *const CGAffineTransform,
x: CGFloat,
y: CGFloat,
radius: CGFloat,
startAngle: CGFloat,
endAngle: CGFloat,
clockwise: bool,
);
pub fn CGPathAddArcToPoint(
path: CGMutablePathRef,
transform: *const CGAffineTransform,
x1: CGFloat,
y1: CGFloat,
x2: CGFloat,
y2: CGFloat,
radius: CGFloat,
);
pub fn CGPathAddPath(path1: CGMutablePathRef, transform: *const CGAffineTransform, path2: CGPathRef);
pub fn CGPathIsEmpty(path: CGPathRef) -> bool;
pub fn CGPathIsRect(path: CGPathRef, rect: *mut CGRect) -> bool;
pub fn CGPathGetCurrentPoint(path: CGPathRef) -> CGPoint;
pub fn CGPathGetBoundingBox(path: CGPathRef) -> CGRect;
pub fn CGPathGetPathBoundingBox(path: CGPathRef) -> CGRect;
pub fn CGPathContainsPoint(path: CGPathRef, transform: *const CGAffineTransform, point: CGPoint, eoFill: bool) -> bool;
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CGPathElementType {
#[doc(alias = "kCGPathElementMoveToPoint")]
MoveToPoint,
#[doc(alias = "kCGPathElementAddLineToPoint")]
AddLineToPoint,
#[doc(alias = "kCGPathElementAddQuadCurveToPoint")]
AddQuadCurveToPoint,
#[doc(alias = "kCGPathElementAddCurveToPoint")]
AddCurveToPoint,
#[doc(alias = "kCGPathElementCloseSubpath")]
CloseSubpath,
}
#[repr(C)]
pub struct CGPathElement {
pub element_type: CGPathElementType,
points: *mut CGPoint,
}
pub type CGPathApplierFunction = extern "C" fn(*mut c_void, *const CGPathElement);
extern "C" {
pub fn CGPathApply(path: CGPathRef, info: *mut c_void, function: CGPathApplierFunction);
pub fn CGPathCreateCopyByNormalizingPath(path: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyByUnioningPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyByIntersectingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyBySubtractingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyBySymmetricDifferenceOfPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyOfLineBySubtractingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyOfLineByIntersectingPath(path: CGPathRef, maskPath: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateSeparateComponents(path: CGPathRef, evenOddFillRule: bool) -> CGPathRef;
pub fn CGPathCreateCopyByFlattening(path: CGPathRef, flatteningThreshold: CGFloat) -> CGPathRef;
pub fn CGPathIntersectsPath(path1: CGPathRef, path2: CGPathRef, evenOddFillRule: bool) -> bool;
}
pub struct CGPath(CGPathRef);
impl Drop for CGPath {
fn drop(&mut self) {
unsafe { CGPathRelease(self.0) }
}
}
impl_TCFType!(CGPath, CGPathRef, CGPathGetTypeID);
impl_CFTypeDescription!(CGPath);
pub struct CGMutablePath(CGMutablePathRef);
impl Drop for CGMutablePath {
fn drop(&mut self) {
unsafe { CGPathRelease(self.0) }
}
}
impl_TCFType!(CGMutablePath, CGMutablePathRef, CGPathGetTypeID);
impl_CFTypeDescription!(CGMutablePath);
impl CGPathElement {
pub fn points(&self) -> &[CGPoint] {
unsafe {
match self.element_type {
CGPathElementType::MoveToPoint | CGPathElementType::AddLineToPoint => slice::from_raw_parts(self.points, 1),
CGPathElementType::AddQuadCurveToPoint => slice::from_raw_parts(self.points, 2),
CGPathElementType::AddCurveToPoint => slice::from_raw_parts(self.points, 3),
CGPathElementType::CloseSubpath => &[],
}
}
}
}
impl Debug for CGPathElement {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
write!(formatter, "{:?}: {:?}", self.element_type, self.points())
}
}
pub struct CGPathElementRef {
element: *const CGPathElement,
}
impl CGPathElementRef {
fn new(element: *const CGPathElement) -> CGPathElementRef {
CGPathElementRef {
element,
}
}
}
impl Deref for CGPathElementRef {
type Target = CGPathElement;
fn deref(&self) -> &CGPathElement {
unsafe { &*self.element }
}
}
impl CGPath {
pub fn new_copy(&self) -> Option<CGPath> {
unsafe {
let path = CGPathCreateCopy(self.as_concrete_TypeRef());
if path.is_null() {
None
} else {
Some(TCFType::wrap_under_create_rule(path))
}
}
}
pub fn new_copy_by_transforming_path(&self, transform: Option<&CGAffineTransform>) -> Option<CGPath> {
unsafe {
let path = CGPathCreateCopyByTransformingPath(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform));
if path.is_null() {
None
} else {
Some(TCFType::wrap_under_create_rule(path))
}
}
}
pub fn new_mutable_copy(&self) -> Option<CGMutablePath> {
unsafe {
let path = CGPathCreateMutableCopy(self.as_concrete_TypeRef());
if path.is_null() {
None
} else {
Some(TCFType::wrap_under_create_rule(path))
}
}
}
pub fn new_mutable_copy_by_transforming_path(&self, transform: Option<&CGAffineTransform>) -> Option<CGMutablePath> {
unsafe {
let path =
CGPathCreateMutableCopyByTransformingPath(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform));
if path.is_null() {
None
} else {
Some(TCFType::wrap_under_create_rule(path))
}
}
}
pub fn from_rect(rect: CGRect, transform: Option<&CGAffineTransform>) -> CGPath {
unsafe {
let path = CGPathCreateWithRect(rect, transform.map_or(null(), |t| t as *const CGAffineTransform));
TCFType::wrap_under_create_rule(path)
}
}
pub fn from_ellipse_in_rect(rect: CGRect, transform: Option<&CGAffineTransform>) -> CGPath {
unsafe {
let path = CGPathCreateWithEllipseInRect(rect, transform.map_or(null(), |t| t as *const CGAffineTransform));
TCFType::wrap_under_create_rule(path)
}
}
pub fn from_rounded_rect(rect: CGRect, corner_width: CGFloat, corner_height: CGFloat, transform: Option<&CGAffineTransform>) -> CGPath {
unsafe {
let path = CGPathCreateWithRoundedRect(rect, corner_width, corner_height, transform.map_or(null(), |t| t as *const CGAffineTransform));
TCFType::wrap_under_create_rule(path)
}
}
pub fn new_copy_by_dashing_path(&self, transform: Option<&CGAffineTransform>, phase: CGFloat, lengths: &[CGFloat]) -> Option<CGPath> {
unsafe {
let path = CGPathCreateCopyByDashingPath(
self.as_concrete_TypeRef(),
transform.map_or(null(), |t| t as *const CGAffineTransform),
phase,
lengths.as_ptr(),
lengths.len(),
);
if path.is_null() {
None
} else {
Some(TCFType::wrap_under_create_rule(path))
}
}
}
pub fn new_copy_by_stroking_path(
&self,
transform: Option<&CGAffineTransform>,
line_width: CGFloat,
line_cap: CGLineCap,
line_join: CGLineJoin,
miter_limit: CGFloat,
) -> Option<CGPath> {
unsafe {
let path = CGPathCreateCopyByStrokingPath(
self.as_concrete_TypeRef(),
transform.map_or(null(), |t| t as *const CGAffineTransform),
line_width,
line_cap,
line_join,
miter_limit,
);
if path.is_null() {
None
} else {
Some(TCFType::wrap_under_create_rule(path))
}
}
}
pub fn equal(&self, path: &CGPath) -> bool {
unsafe { CGPathEqualToPath(self.as_concrete_TypeRef(), path.as_concrete_TypeRef()) }
}
pub fn current_point(&self) -> CGPoint {
unsafe { CGPathGetCurrentPoint(self.as_concrete_TypeRef()) }
}
pub fn bounding_box(&self) -> CGRect {
unsafe { CGPathGetBoundingBox(self.as_concrete_TypeRef()) }
}
pub fn path_bounding_box(&self) -> CGRect {
unsafe { CGPathGetPathBoundingBox(self.as_concrete_TypeRef()) }
}
pub fn is_empty(&self) -> bool {
unsafe { CGPathIsEmpty(self.as_concrete_TypeRef()) }
}
pub fn is_rect(&self) -> Option<CGRect> {
let mut rect = CGRectZero;
let result = unsafe { CGPathIsRect(self.as_concrete_TypeRef(), &mut rect) };
if result {
Some(rect)
} else {
None
}
}
pub fn contains_point(&self, transform: Option<&CGAffineTransform>, point: CGPoint, eo_fill: bool) -> bool {
unsafe { CGPathContainsPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), point, eo_fill) }
}
pub fn apply<F>(&self, closure: F)
where
F: FnMut(CGPathElementRef),
{
unsafe {
CGPathApply(self.as_concrete_TypeRef(), &closure as *const _ as *mut c_void, callback::<F>);
}
extern "C" fn callback<F>(info: *mut c_void, element: *const CGPathElement)
where
F: FnMut(CGPathElementRef),
{
unsafe {
let closure = info as *mut F;
(*closure)(CGPathElementRef::new(element));
}
}
}
}
impl CGMutablePath {
pub fn new() -> CGMutablePath {
unsafe { TCFType::wrap_under_create_rule(CGPathCreateMutable()) }
}
pub fn add_rounded_rect(&self, transform: Option<&CGAffineTransform>, rect: CGRect, corner_width: CGFloat, corner_height: CGFloat) {
unsafe {
CGPathAddRoundedRect(
self.as_concrete_TypeRef(),
transform.map_or(null(), |t| t as *const CGAffineTransform),
rect,
corner_width,
corner_height,
)
}
}
pub fn add_line_to_point(&self, transform: Option<&CGAffineTransform>, x: CGFloat, y: CGFloat) {
unsafe { CGPathAddLineToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), x, y) }
}
pub fn add_quad_curve_to_point(&self, transform: Option<&CGAffineTransform>, cpx: CGFloat, cpy: CGFloat, x: CGFloat, y: CGFloat) {
unsafe { CGPathAddQuadCurveToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), cpx, cpy, x, y) }
}
pub fn add_curve_to_point(
&self,
transform: Option<&CGAffineTransform>,
cp1x: CGFloat,
cp1y: CGFloat,
cp2x: CGFloat,
cp2y: CGFloat,
x: CGFloat,
y: CGFloat,
) {
unsafe {
CGPathAddCurveToPoint(
self.as_concrete_TypeRef(),
transform.map_or(null(), |t| t as *const CGAffineTransform),
cp1x,
cp1y,
cp2x,
cp2y,
x,
y,
)
}
}
pub fn add_rect(&self, transform: Option<&CGAffineTransform>, rect: CGRect) {
unsafe { CGPathAddRect(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), rect) }
}
pub fn add_rects(&self, transform: Option<&CGAffineTransform>, rects: &[CGRect]) {
unsafe {
CGPathAddRects(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), rects.as_ptr(), rects.len())
}
}
pub fn add_lines(&self, transform: Option<&CGAffineTransform>, points: &[CGPoint]) {
unsafe {
CGPathAddLines(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), points.as_ptr(), points.len())
}
}
pub fn add_ellipse_in_rect(&self, transform: Option<&CGAffineTransform>, rect: CGRect) {
unsafe { CGPathAddEllipseInRect(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), rect) }
}
pub fn add_relative_arc(
&self,
transform: Option<&CGAffineTransform>,
x: CGFloat,
y: CGFloat,
radius: CGFloat,
start_angle: CGFloat,
delta: CGFloat,
) {
unsafe {
CGPathAddRelativeArc(
self.as_concrete_TypeRef(),
transform.map_or(null(), |t| t as *const CGAffineTransform),
x,
y,
radius,
start_angle,
delta,
)
}
}
pub fn add_arc(
&self,
transform: Option<&CGAffineTransform>,
x: CGFloat,
y: CGFloat,
radius: CGFloat,
start_angle: CGFloat,
end_angle: CGFloat,
clockwise: bool,
) {
unsafe {
CGPathAddArc(
self.as_concrete_TypeRef(),
transform.map_or(null(), |t| t as *const CGAffineTransform),
x,
y,
radius,
start_angle,
end_angle,
clockwise,
)
}
}
pub fn add_arc_to_point(&self, transform: Option<&CGAffineTransform>, x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat, radius: CGFloat) {
unsafe {
CGPathAddArcToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), x1, y1, x2, y2, radius)
}
}
pub fn add_path(&self, transform: Option<&CGAffineTransform>, path: &CGPath) {
unsafe { CGPathAddPath(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), path.as_concrete_TypeRef()) }
}
pub fn close_subpath(&self) {
unsafe { CGPathCloseSubpath(self.as_concrete_TypeRef()) }
}
pub fn move_to_point(&self, transform: Option<&CGAffineTransform>, x: CGFloat, y: CGFloat) {
unsafe { CGPathMoveToPoint(self.as_concrete_TypeRef(), transform.map_or(null(), |t| t as *const CGAffineTransform), x, y) }
}
pub fn to_immutable(&self) -> CGPath {
unsafe { CGPath::wrap_under_get_rule(self.as_concrete_TypeRef()) }
}
}
#[cfg(feature = "objc")]
unsafe impl RefEncode for __CGPath {
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("CGPath", &[]));
}