core-graphics2 0.5.2

Safe bindings to CoreGraphics framework, including display stream
Documentation
use std::{ptr::null, slice::from_raw_parts};

use core_foundation::{
    base::{CFType, CFTypeID, TCFType},
    dictionary::{CFDictionary, CFDictionaryRef},
    impl_CFTypeDescription, impl_TCFType,
    string::{CFString, CFStringRef},
};
use libc::{c_void, size_t};
#[cfg(feature = "objc")]
use objc2::encode::{Encoding, RefEncode};

use crate::{
    base::CGFloat,
    color_space::{CGColorRenderingIntent, CGColorSpace, CGColorSpaceRef},
    pattern::{CGPattern, CGPatternRef},
};

#[repr(C)]
pub struct __CGColor(c_void);

pub type CGColorRef = *mut __CGColor;

extern "C" {
    pub fn CGColorCreate(color_space: CGColorSpaceRef, components: *const CGFloat) -> CGColorRef;
    pub fn CGColorCreateGenericGray(gray: CGFloat, alpha: CGFloat) -> CGColorRef;
    pub fn CGColorCreateGenericRGB(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> CGColorRef;
    pub fn CGColorCreateGenericCMYK(cyan: CGFloat, magenta: CGFloat, yellow: CGFloat, black: CGFloat, alpha: CGFloat) -> CGColorRef;
    pub fn CGColorCreateGenericGrayGamma2_2(gray: CGFloat, alpha: CGFloat) -> CGColorRef;
    pub fn CGColorCreateSRGB(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> CGColorRef;
    pub fn CGColorGetConstantColor(colorName: CFStringRef) -> CGColorRef;
    pub fn CGColorCreateWithPattern(space: CGColorSpaceRef, pattern: CGPatternRef, components: *const CGFloat) -> CGColorRef;
    pub fn CGColorCreateCopy(color: CGColorRef) -> CGColorRef;
    pub fn CGColorCreateCopyWithAlpha(color: CGColorRef, alpha: CGFloat) -> CGColorRef;
    pub fn CGColorCreateCopyByMatchingToColorSpace(
        space: CGColorSpaceRef,
        intent: CGColorRenderingIntent,
        color: CGColorRef,
        options: CFDictionaryRef,
    ) -> CGColorRef;
    pub fn CGColorRetain(color: CGColorRef) -> CGColorRef;
    pub fn CGColorRelease(color: CGColorRef);
    pub fn CGColorEqualToColor(color1: CGColorRef, color2: CGColorRef) -> bool;
    pub fn CGColorGetNumberOfComponents(color: CGColorRef) -> size_t;
    pub fn CGColorGetComponents(color: CGColorRef) -> *const CGFloat;
    pub fn CGColorGetAlpha(color: CGColorRef) -> CGFloat;
    pub fn CGColorGetColorSpace(color: CGColorRef) -> CGColorSpaceRef;
    pub fn CGColorGetPattern(color: CGColorRef) -> CGPatternRef;
    pub fn CGColorGetTypeID() -> CFTypeID;

    pub static kCGColorWhite: CFStringRef;
    pub static kCGColorBlack: CFStringRef;
    pub static kCGColorClear: CFStringRef;
}

pub struct CGColor(CGColorRef);

impl Drop for CGColor {
    fn drop(&mut self) {
        unsafe { CGColorRelease(self.0) }
    }
}

impl_TCFType!(CGColor, CGColorRef, CGColorGetTypeID);
impl_CFTypeDescription!(CGColor);

impl CGColor {
    pub fn new(color_space: &CGColorSpace, components: &[CGFloat]) -> Option<Self> {
        unsafe {
            let color = CGColorCreate(color_space.as_concrete_TypeRef(), components.as_ptr());
            if color.is_null() {
                None
            } else {
                Some(TCFType::wrap_under_create_rule(color))
            }
        }
    }

    pub fn new_generic_gray(gray: CGFloat, alpha: CGFloat) -> Self {
        unsafe {
            let color = CGColorCreateGenericGray(gray, alpha);
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_generic_rgb(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> Self {
        unsafe {
            let color = CGColorCreateGenericRGB(red, green, blue, alpha);
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_generic_cmyk(cyan: CGFloat, magenta: CGFloat, yellow: CGFloat, black: CGFloat, alpha: CGFloat) -> Self {
        unsafe {
            let color = CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha);
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_generic_gray_gamma_2_2(gray: CGFloat, alpha: CGFloat) -> Self {
        unsafe {
            let color = CGColorCreateGenericGrayGamma2_2(gray, alpha);
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_srgb(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> Self {
        unsafe {
            let color = CGColorCreateSRGB(red, green, blue, alpha);
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_copy(color: &CGColor) -> Self {
        unsafe {
            let color = CGColorCreateCopy(color.as_concrete_TypeRef());
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_copy_with_alpha(color: &CGColor, alpha: CGFloat) -> Self {
        unsafe {
            let color = CGColorCreateCopyWithAlpha(color.as_concrete_TypeRef(), alpha);
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn new_copy_by_matching_to_color_space(
        space: &CGColorSpace,
        intent: CGColorRenderingIntent,
        color: &CGColor,
        options: Option<&CFDictionary<CFString, CFType>>,
    ) -> Self {
        unsafe {
            let color = CGColorCreateCopyByMatchingToColorSpace(
                space.as_concrete_TypeRef(),
                intent,
                color.as_concrete_TypeRef(),
                options.map_or(null(), |o| o.as_concrete_TypeRef()),
            );
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn from_pattern(space: &CGColorSpace, pattern: Option<&CGPattern>, components: Option<&[CGFloat]>) -> Self {
        unsafe {
            let color = CGColorCreateWithPattern(
                space.as_concrete_TypeRef(),
                pattern.map_or(null(), |p| p.as_concrete_TypeRef()),
                components.map_or(null(), |c| c.as_ptr()),
            );
            TCFType::wrap_under_create_rule(color)
        }
    }

    pub fn constant_color(color_name: &CFString) -> Option<Self> {
        unsafe {
            let color = CGColorGetConstantColor(color_name.as_concrete_TypeRef());
            if color.is_null() {
                None
            } else {
                Some(TCFType::wrap_under_create_rule(color))
            }
        }
    }

    pub fn equal(&self, color: &CGColor) -> bool {
        unsafe { CGColorEqualToColor(self.as_concrete_TypeRef(), color.as_concrete_TypeRef()) }
    }

    pub fn number_of_components(&self) -> size_t {
        unsafe { CGColorGetNumberOfComponents(self.as_concrete_TypeRef()) }
    }

    pub fn components(&self) -> &[CGFloat] {
        unsafe {
            let ptr = CGColorGetComponents(self.as_concrete_TypeRef());
            let count = self.number_of_components();
            if ptr.is_null() || count == 0 {
                &[]
            } else {
                from_raw_parts(ptr, count)
            }
        }
    }

    pub fn alpha(&self) -> CGFloat {
        unsafe { CGColorGetAlpha(self.as_concrete_TypeRef()) }
    }

    pub fn color_space(&self) -> Option<CGColorSpace> {
        unsafe {
            let space = CGColorGetColorSpace(self.as_concrete_TypeRef());
            if space.is_null() {
                None
            } else {
                Some(TCFType::wrap_under_get_rule(space))
            }
        }
    }

    pub fn pattern(&self) -> Option<CGPattern> {
        unsafe {
            let pattern = CGColorGetPattern(self.as_concrete_TypeRef());
            if pattern.is_null() {
                None
            } else {
                Some(TCFType::wrap_under_get_rule(pattern))
            }
        }
    }
}

pub enum CGColorName {
    White,
    Black,
    Clear,
}

impl From<CGColorName> for CFStringRef {
    fn from(name: CGColorName) -> Self {
        unsafe {
            match name {
                CGColorName::White => kCGColorWhite,
                CGColorName::Black => kCGColorBlack,
                CGColorName::Clear => kCGColorClear,
            }
        }
    }
}

impl From<CGColorName> for CFString {
    fn from(name: CGColorName) -> Self {
        unsafe { CFString::wrap_under_get_rule(CFStringRef::from(name)) }
    }
}

#[cfg(feature = "objc")]
unsafe impl RefEncode for __CGColor {
    const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("CGColor", &[]));
}