1use core::ffi::c_void;
8use core::ptr;
9use std::ffi::CString;
10use std::io;
11use std::os::unix::ffi::OsStrExt;
12use std::path::Path;
13
14use crate::ffi as bridge_ffi;
15
16use super::ffi as cg_ffi;
17
18pub struct CGColorSpace {
20 ptr: *mut c_void,
21}
22
23unsafe impl Send for CGColorSpace {}
27unsafe impl Sync for CGColorSpace {}
28
29crate::utils::retained::cf_retained!(
30 CGColorSpace,
31 field = ptr,
32 retain = cg_ffi::CGColorSpaceRetain,
33 release = cg_ffi::CGColorSpaceRelease,
34 drop = null_out,
35);
36
37impl std::fmt::Debug for CGColorSpace {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("CGColorSpace")
40 .field("ptr", &self.ptr)
41 .finish()
42 }
43}
44
45impl CGColorSpace {
46 #[must_use]
53 pub const unsafe fn from_raw(ptr: *mut c_void) -> Self {
54 Self { ptr }
55 }
56
57 #[must_use]
59 pub fn device_rgb() -> Self {
60 Self {
61 ptr: unsafe { cg_ffi::CGColorSpaceCreateDeviceRGB() },
62 }
63 }
64
65 #[must_use]
67 pub fn device_gray() -> Self {
68 Self {
69 ptr: unsafe { cg_ffi::CGColorSpaceCreateDeviceGray() },
70 }
71 }
72
73 #[must_use]
75 pub fn srgb() -> Self {
76 unsafe {
77 let n = CFStringCreateWithCStringLite(b"kCGColorSpaceSRGB\0".as_ptr());
78 let p = cg_ffi::CGColorSpaceCreateWithName(n);
79 CFReleaseLite(n);
80 Self { ptr: p }
81 }
82 }
83
84 #[must_use]
86 pub fn display_p3() -> Self {
87 unsafe {
88 let n = CFStringCreateWithCStringLite(b"kCGColorSpaceDisplayP3\0".as_ptr());
89 let p = cg_ffi::CGColorSpaceCreateWithName(n);
90 CFReleaseLite(n);
91 Self { ptr: p }
92 }
93 }
94
95 #[must_use]
97 pub fn number_of_components(&self) -> usize {
98 unsafe { cg_ffi::CGColorSpaceGetNumberOfComponents(self.ptr) }
99 }
100
101 #[must_use]
103 pub const fn as_ptr(&self) -> *mut c_void {
104 self.ptr
105 }
106}
107
108pub struct CGImage {
110 ptr: *mut c_void,
111}
112
113unsafe impl Send for CGImage {}
117unsafe impl Sync for CGImage {}
118
119crate::utils::retained::cf_retained!(
120 CGImage,
121 field = ptr,
122 retain = cg_ffi::CGImageRetain,
123 release = cg_ffi::CGImageRelease,
124 drop = null_out,
125);
126
127impl std::fmt::Debug for CGImage {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 f.debug_struct("CGImage").field("ptr", &self.ptr).finish()
130 }
131}
132
133impl CGImage {
134 #[must_use]
141 pub const unsafe fn from_raw(ptr: *mut c_void) -> Self {
142 Self { ptr }
143 }
144
145 #[must_use]
147 pub fn width(&self) -> usize {
148 unsafe { cg_ffi::CGImageGetWidth(self.ptr) }
149 }
150
151 #[must_use]
153 pub fn height(&self) -> usize {
154 unsafe { cg_ffi::CGImageGetHeight(self.ptr) }
155 }
156
157 #[must_use]
159 pub fn bits_per_component(&self) -> usize {
160 unsafe { cg_ffi::CGImageGetBitsPerComponent(self.ptr) }
161 }
162
163 #[must_use]
165 pub fn bits_per_pixel(&self) -> usize {
166 unsafe { cg_ffi::CGImageGetBitsPerPixel(self.ptr) }
167 }
168
169 #[must_use]
171 pub fn bytes_per_row(&self) -> usize {
172 unsafe { cg_ffi::CGImageGetBytesPerRow(self.ptr) }
173 }
174
175 pub fn save_png<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
182 let c_path = CString::new(path.as_ref().as_os_str().as_bytes()).map_err(|_| {
183 io::Error::new(
184 io::ErrorKind::InvalidInput,
185 "path contains an interior NUL byte",
186 )
187 })?;
188
189 if unsafe { bridge_ffi::cgimage_save_png(self.ptr, c_path.as_ptr()) } {
190 Ok(())
191 } else {
192 Err(io::Error::other("cgimage_save_png returned false"))
193 }
194 }
195
196 #[must_use]
198 pub const fn as_ptr(&self) -> *mut c_void {
199 self.ptr
200 }
201}
202
203extern "C" {
204 fn CFStringCreateWithCString(
205 allocator: *const c_void,
206 bytes: *const u8,
207 encoding: u32,
208 ) -> *mut c_void;
209 fn CFRelease(cf: *const c_void);
210}
211
212#[allow(non_snake_case)]
213unsafe fn CFStringCreateWithCStringLite(bytes: *const u8) -> *const c_void {
214 CFStringCreateWithCString(ptr::null(), bytes, 0x0800_0100).cast_const()
215}
216
217#[allow(non_snake_case)]
218unsafe fn CFReleaseLite(p: *const c_void) {
219 CFRelease(p);
220}