screencapturekit/output/
iosurface.rs1use std::ffi::c_void;
6use std::io;
7
8#[repr(u32)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum IOSurfaceLockOptions {
12 ReadOnly = 0x0000_0001,
14 AvoidSync = 0x0000_0002,
16}
17
18impl IOSurfaceLockOptions {
19 pub const fn as_u32(self) -> u32 {
20 self as u32
21 }
22}
23
24pub struct IOSurfaceLockGuard<'a> {
28 surface_ptr: *const c_void,
29 base_address: std::ptr::NonNull<u8>,
30 width: usize,
31 height: usize,
32 bytes_per_row: usize,
33 options: IOSurfaceLockOptions,
34 _phantom: std::marker::PhantomData<&'a IOSurface>,
35}
36
37impl IOSurfaceLockGuard<'_> {
38 pub(crate) unsafe fn new(
40 surface_ptr: *const c_void,
41 options: IOSurfaceLockOptions,
42 width: usize,
43 height: usize,
44 bytes_per_row: usize,
45 ) -> Result<Self, i32> {
46 let result = crate::ffi::iosurface_lock(surface_ptr, options.as_u32());
48 if result != 0 {
49 return Err(result);
50 }
51
52 let base_ptr = crate::ffi::iosurface_get_base_address(surface_ptr);
54 let base_address = std::ptr::NonNull::new(base_ptr).ok_or(-1)?;
55
56 Ok(Self {
57 surface_ptr,
58 base_address,
59 width,
60 height,
61 bytes_per_row,
62 options,
63 _phantom: std::marker::PhantomData,
64 })
65 }
66
67 pub const fn width(&self) -> usize {
69 self.width
70 }
71
72 pub const fn height(&self) -> usize {
74 self.height
75 }
76
77 pub const fn bytes_per_row(&self) -> usize {
79 self.bytes_per_row
80 }
81
82 pub const fn as_ptr(&self) -> *const u8 {
84 self.base_address.as_ptr()
85 }
86
87 pub fn as_mut_ptr(&mut self) -> *mut u8 {
89 self.base_address.as_ptr()
90 }
91
92 pub fn as_slice(&self) -> &[u8] {
94 unsafe {
95 std::slice::from_raw_parts(self.base_address.as_ptr(), self.height * self.bytes_per_row)
96 }
97 }
98
99 pub fn row(&self, row_index: usize) -> Option<&[u8]> {
101 if row_index >= self.height {
102 return None;
103 }
104 unsafe {
105 let row_ptr = self
106 .base_address
107 .as_ptr()
108 .add(row_index * self.bytes_per_row);
109 Some(std::slice::from_raw_parts(row_ptr, self.bytes_per_row))
110 }
111 }
112
113 pub fn cursor(&self) -> io::Cursor<&[u8]> {
136 io::Cursor::new(self.as_slice())
137 }
138
139 pub fn with_cursor<F, R>(&self, f: F) -> R
144 where
145 F: FnOnce(io::Cursor<&[u8]>) -> R,
146 {
147 f(self.cursor())
148 }
149}
150
151impl Drop for IOSurfaceLockGuard<'_> {
152 fn drop(&mut self) {
153 unsafe {
154 crate::ffi::iosurface_unlock(self.surface_ptr, self.options.as_u32());
155 }
156 }
157}
158
159impl std::ops::Deref for IOSurfaceLockGuard<'_> {
160 type Target = [u8];
161
162 fn deref(&self) -> &Self::Target {
163 self.as_slice()
164 }
165}
166
167pub struct IOSurface(*const c_void);
172
173impl PartialEq for IOSurface {
174 fn eq(&self, other: &Self) -> bool {
175 self.0 == other.0
176 }
177}
178
179impl Eq for IOSurface {}
180
181impl std::hash::Hash for IOSurface {
182 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
183 self.0.hash(state);
184 }
185}
186
187impl IOSurface {
188 pub(crate) unsafe fn from_ptr(ptr: *const c_void) -> Option<Self> {
190 if ptr.is_null() {
191 None
192 } else {
193 Some(Self(ptr))
194 }
195 }
196
197 pub fn width(&self) -> usize {
199 #[allow(clippy::cast_sign_loss)]
201 unsafe {
202 crate::ffi::iosurface_get_width(self.0) as usize
203 }
204 }
205
206 pub fn height(&self) -> usize {
208 #[allow(clippy::cast_sign_loss)]
210 unsafe {
211 crate::ffi::iosurface_get_height(self.0) as usize
212 }
213 }
214
215 pub fn bytes_per_row(&self) -> usize {
217 #[allow(clippy::cast_sign_loss)]
219 unsafe {
220 crate::ffi::iosurface_get_bytes_per_row(self.0) as usize
221 }
222 }
223
224 pub fn pixel_format(&self) -> u32 {
226 unsafe { crate::ffi::iosurface_get_pixel_format(self.0) }
227 }
228
229 pub unsafe fn base_address(&self) -> *mut u8 {
238 crate::ffi::iosurface_get_base_address(self.0)
239 }
240
241 pub fn lock(&self, options: IOSurfaceLockOptions) -> Result<IOSurfaceLockGuard<'_>, i32> {
262 unsafe {
263 IOSurfaceLockGuard::new(
264 self.0,
265 options,
266 self.width(),
267 self.height(),
268 self.bytes_per_row(),
269 )
270 }
271 }
272
273 pub fn is_in_use(&self) -> bool {
275 unsafe { crate::ffi::iosurface_is_in_use(self.0) }
276 }
277}
278
279impl Drop for IOSurface {
280 fn drop(&mut self) {
281 if !self.0.is_null() {
282 unsafe {
283 crate::ffi::iosurface_release(self.0);
284 }
285 }
286 }
287}
288
289unsafe impl Send for IOSurface {}
290unsafe impl Sync for IOSurface {}
291
292impl std::fmt::Debug for IOSurface {
293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 f.debug_struct("IOSurface")
295 .field("width", &self.width())
296 .field("height", &self.height())
297 .field("bytes_per_row", &self.bytes_per_row())
298 .field("pixel_format", &format!("0x{:08X}", self.pixel_format()))
299 .field("is_in_use", &self.is_in_use())
300 .finish()
301 }
302}
303
304pub trait CVPixelBufferIOSurface {
306 fn iosurface(&self) -> Option<IOSurface>;
308
309 fn is_backed_by_iosurface(&self) -> bool;
311}
312
313impl CVPixelBufferIOSurface for crate::output::CVPixelBuffer {
314 fn iosurface(&self) -> Option<IOSurface> {
315 unsafe {
316 let ptr = crate::ffi::cv_pixel_buffer_get_iosurface(self.as_ptr());
317 IOSurface::from_ptr(ptr)
318 }
319 }
320
321 fn is_backed_by_iosurface(&self) -> bool {
322 unsafe { crate::ffi::cv_pixel_buffer_is_backed_by_iosurface(self.as_ptr()) }
323 }
324}