screencapturekit/cv/
pixel_buffer.rs

1//! `CVPixelBuffer` - Video pixel buffer
2
3use super::ffi;
4use crate::cm::IOSurface;
5use std::fmt;
6use std::io::{self, Read, Seek, SeekFrom};
7
8/// Lock flags for `CVPixelBuffer`
9///
10/// This is a bitmask type matching Apple's `CVPixelBufferLockFlags`.
11///
12/// # Examples
13///
14/// ```
15/// use screencapturekit::cv::CVPixelBufferLockFlags;
16///
17/// // Read-only lock
18/// let flags = CVPixelBufferLockFlags::READ_ONLY;
19/// assert!(flags.is_read_only());
20///
21/// // Read-write lock (default)
22/// let flags = CVPixelBufferLockFlags::NONE;
23/// assert!(!flags.is_read_only());
24/// ```
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub struct CVPixelBufferLockFlags(u32);
27
28impl CVPixelBufferLockFlags {
29    /// No special options (read-write lock)
30    pub const NONE: Self = Self(0);
31
32    /// Read-only lock - use when you only need to read data.
33    /// This allows Core Video to keep caches valid.
34    pub const READ_ONLY: Self = Self(0x0000_0001);
35
36    /// Create from a raw u32 value
37    #[must_use]
38    pub const fn from_bits(bits: u32) -> Self {
39        Self(bits)
40    }
41
42    /// Convert to u32 for FFI
43    #[must_use]
44    pub const fn as_u32(self) -> u32 {
45        self.0
46    }
47
48    /// Check if this is a read-only lock
49    #[must_use]
50    pub const fn is_read_only(self) -> bool {
51        (self.0 & Self::READ_ONLY.0) != 0
52    }
53
54    /// Check if no flags are set (read-write lock)
55    #[must_use]
56    pub const fn is_empty(self) -> bool {
57        self.0 == 0
58    }
59}
60
61impl From<CVPixelBufferLockFlags> for u32 {
62    fn from(flags: CVPixelBufferLockFlags) -> Self {
63        flags.0
64    }
65}
66
67#[derive(Debug)]
68pub struct CVPixelBuffer(*mut std::ffi::c_void);
69
70impl PartialEq for CVPixelBuffer {
71    fn eq(&self, other: &Self) -> bool {
72        self.0 == other.0
73    }
74}
75
76impl Eq for CVPixelBuffer {}
77
78impl std::hash::Hash for CVPixelBuffer {
79    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
80        unsafe {
81            let hash_value = ffi::cv_pixel_buffer_hash(self.0);
82            hash_value.hash(state);
83        }
84    }
85}
86
87impl CVPixelBuffer {
88    pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
89        if ptr.is_null() {
90            None
91        } else {
92            Some(Self(ptr))
93        }
94    }
95
96    /// # Safety
97    /// The caller must ensure the pointer is a valid `CVPixelBuffer` pointer.
98    pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
99        Self(ptr)
100    }
101
102    pub fn as_ptr(&self) -> *mut std::ffi::c_void {
103        self.0
104    }
105
106    /// Create a new pixel buffer with the specified dimensions and pixel format
107    ///
108    /// # Arguments
109    ///
110    /// * `width` - Width of the pixel buffer in pixels
111    /// * `height` - Height of the pixel buffer in pixels
112    /// * `pixel_format` - Pixel format type (e.g., 0x42475241 for BGRA)
113    ///
114    /// # Errors
115    ///
116    /// Returns a Core Video error code if the pixel buffer creation fails.
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use screencapturekit::cv::CVPixelBuffer;
122    ///
123    /// // Create a 1920x1080 BGRA pixel buffer
124    /// let buffer = CVPixelBuffer::create(1920, 1080, 0x42475241)
125    ///     .expect("Failed to create pixel buffer");
126    ///
127    /// assert_eq!(buffer.width(), 1920);
128    /// assert_eq!(buffer.height(), 1080);
129    /// assert_eq!(buffer.pixel_format(), 0x42475241);
130    /// ```
131    pub fn create(width: usize, height: usize, pixel_format: u32) -> Result<Self, i32> {
132        unsafe {
133            let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
134            let status =
135                ffi::cv_pixel_buffer_create(width, height, pixel_format, &mut pixel_buffer_ptr);
136
137            if status == 0 && !pixel_buffer_ptr.is_null() {
138                Ok(Self(pixel_buffer_ptr))
139            } else {
140                Err(status)
141            }
142        }
143    }
144
145    /// Create a pixel buffer from existing memory
146    ///
147    /// # Arguments
148    ///
149    /// * `width` - Width of the pixel buffer in pixels
150    /// * `height` - Height of the pixel buffer in pixels
151    /// * `pixel_format` - Pixel format type (e.g., 0x42475241 for BGRA)
152    /// * `base_address` - Pointer to pixel data
153    /// * `bytes_per_row` - Number of bytes per row
154    ///
155    /// # Safety
156    ///
157    /// The caller must ensure that:
158    /// - `base_address` points to valid memory
159    /// - Memory remains valid for the lifetime of the pixel buffer
160    /// - `bytes_per_row` correctly represents the memory layout
161    ///
162    /// # Errors
163    ///
164    /// Returns a Core Video error code if the pixel buffer creation fails.
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use screencapturekit::cv::CVPixelBuffer;
170    ///
171    /// // Create pixel data (100x100 BGRA image)
172    /// let width = 100;
173    /// let height = 100;
174    /// let bytes_per_pixel = 4; // BGRA
175    /// let bytes_per_row = width * bytes_per_pixel;
176    /// let mut pixel_data = vec![0u8; width * height * bytes_per_pixel];
177    ///
178    /// // Fill with blue color
179    /// for y in 0..height {
180    ///     for x in 0..width {
181    ///         let offset = y * bytes_per_row + x * bytes_per_pixel;
182    ///         pixel_data[offset] = 255;     // B
183    ///         pixel_data[offset + 1] = 0;   // G
184    ///         pixel_data[offset + 2] = 0;   // R
185    ///         pixel_data[offset + 3] = 255; // A
186    ///     }
187    /// }
188    ///
189    /// // Create pixel buffer from the data
190    /// let buffer = unsafe {
191    ///     CVPixelBuffer::create_with_bytes(
192    ///         width,
193    ///         height,
194    ///         0x42475241, // BGRA
195    ///         pixel_data.as_mut_ptr() as *mut std::ffi::c_void,
196    ///         bytes_per_row,
197    ///     )
198    /// }.expect("Failed to create pixel buffer");
199    ///
200    /// assert_eq!(buffer.width(), width);
201    /// assert_eq!(buffer.height(), height);
202    /// ```
203    pub unsafe fn create_with_bytes(
204        width: usize,
205        height: usize,
206        pixel_format: u32,
207        base_address: *mut std::ffi::c_void,
208        bytes_per_row: usize,
209    ) -> Result<Self, i32> {
210        let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
211        let status = ffi::cv_pixel_buffer_create_with_bytes(
212            width,
213            height,
214            pixel_format,
215            base_address,
216            bytes_per_row,
217            &mut pixel_buffer_ptr,
218        );
219
220        if status == 0 && !pixel_buffer_ptr.is_null() {
221            Ok(Self(pixel_buffer_ptr))
222        } else {
223            Err(status)
224        }
225    }
226
227    /// Fill the extended pixels of a pixel buffer
228    ///
229    /// This is useful for pixel buffers that have been created with extended pixels
230    /// enabled, to ensure proper edge handling for effects and filters.
231    ///
232    /// # Errors
233    ///
234    /// Returns a Core Video error code if the operation fails.
235    pub fn fill_extended_pixels(&self) -> Result<(), i32> {
236        unsafe {
237            let status = ffi::cv_pixel_buffer_fill_extended_pixels(self.0);
238            if status == 0 {
239                Ok(())
240            } else {
241                Err(status)
242            }
243        }
244    }
245
246    /// Create a pixel buffer with planar bytes
247    ///
248    /// # Safety
249    ///
250    /// The caller must ensure that:
251    /// - `plane_base_addresses` points to valid memory for each plane
252    /// - Memory remains valid for the lifetime of the pixel buffer
253    /// - All plane parameters correctly represent the memory layout
254    ///
255    /// # Errors
256    ///
257    /// Returns a Core Video error code if the pixel buffer creation fails.
258    pub unsafe fn create_with_planar_bytes(
259        width: usize,
260        height: usize,
261        pixel_format: u32,
262        plane_base_addresses: &[*mut std::ffi::c_void],
263        plane_widths: &[usize],
264        plane_heights: &[usize],
265        plane_bytes_per_row: &[usize],
266    ) -> Result<Self, i32> {
267        if plane_base_addresses.len() != plane_widths.len()
268            || plane_widths.len() != plane_heights.len()
269            || plane_heights.len() != plane_bytes_per_row.len()
270        {
271            return Err(-50); // paramErr
272        }
273
274        let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
275        let status = ffi::cv_pixel_buffer_create_with_planar_bytes(
276            width,
277            height,
278            pixel_format,
279            plane_base_addresses.len(),
280            plane_base_addresses.as_ptr(),
281            plane_widths.as_ptr(),
282            plane_heights.as_ptr(),
283            plane_bytes_per_row.as_ptr(),
284            &mut pixel_buffer_ptr,
285        );
286
287        if status == 0 && !pixel_buffer_ptr.is_null() {
288            Ok(Self(pixel_buffer_ptr))
289        } else {
290            Err(status)
291        }
292    }
293
294    /// Create a pixel buffer from an `IOSurface`
295    ///
296    /// # Errors
297    ///
298    /// Returns a Core Video error code if the pixel buffer creation fails.
299    pub fn create_with_io_surface(surface: &IOSurface) -> Result<Self, i32> {
300        unsafe {
301            let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
302            let status = ffi::cv_pixel_buffer_create_with_io_surface(
303                surface.as_ptr(),
304                &mut pixel_buffer_ptr,
305            );
306
307            if status == 0 && !pixel_buffer_ptr.is_null() {
308                Ok(Self(pixel_buffer_ptr))
309            } else {
310                Err(status)
311            }
312        }
313    }
314
315    /// Get the Core Foundation type ID for `CVPixelBuffer`
316    pub fn type_id() -> usize {
317        unsafe { ffi::cv_pixel_buffer_get_type_id() }
318    }
319
320    /// Get the data size of the pixel buffer
321    pub fn data_size(&self) -> usize {
322        unsafe { ffi::cv_pixel_buffer_get_data_size(self.0) }
323    }
324
325    /// Check if the pixel buffer is planar
326    pub fn is_planar(&self) -> bool {
327        unsafe { ffi::cv_pixel_buffer_is_planar(self.0) }
328    }
329
330    /// Get the number of planes in the pixel buffer
331    pub fn plane_count(&self) -> usize {
332        unsafe { ffi::cv_pixel_buffer_get_plane_count(self.0) }
333    }
334
335    /// Get the width of a specific plane
336    pub fn width_of_plane(&self, plane_index: usize) -> usize {
337        unsafe { ffi::cv_pixel_buffer_get_width_of_plane(self.0, plane_index) }
338    }
339
340    /// Get the height of a specific plane
341    pub fn height_of_plane(&self, plane_index: usize) -> usize {
342        unsafe { ffi::cv_pixel_buffer_get_height_of_plane(self.0, plane_index) }
343    }
344
345    /// Get the base address of a specific plane (internal use only)
346    ///
347    /// # Safety
348    /// Caller must ensure the buffer is locked before accessing the returned pointer.
349    fn base_address_of_plane_raw(&self, plane_index: usize) -> Option<*mut u8> {
350        unsafe {
351            let ptr = ffi::cv_pixel_buffer_get_base_address_of_plane(self.0, plane_index);
352            if ptr.is_null() {
353                None
354            } else {
355                Some(ptr.cast::<u8>())
356            }
357        }
358    }
359
360    /// Get the bytes per row of a specific plane
361    pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
362        unsafe { ffi::cv_pixel_buffer_get_bytes_per_row_of_plane(self.0, plane_index) }
363    }
364
365    /// Get the extended pixel information (left, right, top, bottom)
366    pub fn extended_pixels(&self) -> (usize, usize, usize, usize) {
367        unsafe {
368            let mut left: usize = 0;
369            let mut right: usize = 0;
370            let mut top: usize = 0;
371            let mut bottom: usize = 0;
372            ffi::cv_pixel_buffer_get_extended_pixels(
373                self.0,
374                &mut left,
375                &mut right,
376                &mut top,
377                &mut bottom,
378            );
379            (left, right, top, bottom)
380        }
381    }
382
383    /// Check if the pixel buffer is backed by an `IOSurface`
384    pub fn is_backed_by_io_surface(&self) -> bool {
385        self.io_surface().is_some()
386    }
387
388    /// Get the width of the pixel buffer in pixels
389    pub fn width(&self) -> usize {
390        unsafe { ffi::cv_pixel_buffer_get_width(self.0) }
391    }
392
393    pub fn height(&self) -> usize {
394        unsafe { ffi::cv_pixel_buffer_get_height(self.0) }
395    }
396
397    pub fn pixel_format(&self) -> u32 {
398        unsafe { ffi::cv_pixel_buffer_get_pixel_format_type(self.0) }
399    }
400
401    pub fn bytes_per_row(&self) -> usize {
402        unsafe { ffi::cv_pixel_buffer_get_bytes_per_row(self.0) }
403    }
404
405    /// Lock the base address for raw access
406    ///
407    /// # Errors
408    ///
409    /// Returns a Core Video error code if the lock operation fails.
410    pub fn lock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
411        unsafe {
412            let result = ffi::cv_pixel_buffer_lock_base_address(self.0, flags.as_u32());
413            if result == 0 {
414                Ok(())
415            } else {
416                Err(result)
417            }
418        }
419    }
420
421    /// Unlock the base address after raw access
422    ///
423    /// # Errors
424    ///
425    /// Returns a Core Video error code if the unlock operation fails.
426    pub fn unlock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
427        unsafe {
428            let result = ffi::cv_pixel_buffer_unlock_base_address(self.0, flags.as_u32());
429            if result == 0 {
430                Ok(())
431            } else {
432                Err(result)
433            }
434        }
435    }
436
437    /// Get the base address (internal use only)
438    ///
439    /// # Safety
440    /// Caller must ensure the buffer is locked before accessing the returned pointer.
441    fn base_address_raw(&self) -> Option<*mut u8> {
442        unsafe {
443            let ptr = ffi::cv_pixel_buffer_get_base_address(self.0);
444            if ptr.is_null() {
445                None
446            } else {
447                Some(ptr.cast::<u8>())
448            }
449        }
450    }
451
452    /// Get the `IOSurface` backing this pixel buffer
453    pub fn io_surface(&self) -> Option<IOSurface> {
454        unsafe {
455            let ptr = ffi::cv_pixel_buffer_get_io_surface(self.0);
456            IOSurface::from_raw(ptr)
457        }
458    }
459
460    /// Lock the base address and return a guard for RAII-style access
461    ///
462    /// # Arguments
463    ///
464    /// * `flags` - Lock flags (use `CVPixelBufferLockFlags::READ_ONLY` for read-only access)
465    ///
466    /// # Errors
467    ///
468    /// Returns a Core Video error code if the lock operation fails.
469    ///
470    /// # Examples
471    ///
472    /// ```no_run
473    /// use screencapturekit::cv::{CVPixelBuffer, CVPixelBufferLockFlags};
474    ///
475    /// fn read_buffer(buffer: &CVPixelBuffer) {
476    ///     let guard = buffer.lock(CVPixelBufferLockFlags::READ_ONLY).unwrap();
477    ///     let data = guard.as_slice();
478    ///     println!("Buffer has {} bytes", data.len());
479    ///     // Buffer automatically unlocked when guard drops
480    /// }
481    /// ```
482    pub fn lock(&self, flags: CVPixelBufferLockFlags) -> Result<CVPixelBufferLockGuard<'_>, i32> {
483        self.lock_raw(flags)?;
484        Ok(CVPixelBufferLockGuard {
485            buffer: self,
486            flags,
487        })
488    }
489
490    /// Lock the base address for read-only access
491    ///
492    /// This is a convenience method equivalent to `lock(CVPixelBufferLockFlags::READ_ONLY)`.
493    ///
494    /// # Errors
495    ///
496    /// Returns a Core Video error code if the lock operation fails.
497    pub fn lock_read_only(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
498        self.lock(CVPixelBufferLockFlags::READ_ONLY)
499    }
500
501    /// Lock the base address for read-write access
502    ///
503    /// This is a convenience method equivalent to `lock(CVPixelBufferLockFlags::NONE)`.
504    ///
505    /// # Errors
506    ///
507    /// Returns a Core Video error code if the lock operation fails.
508    pub fn lock_read_write(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
509        self.lock(CVPixelBufferLockFlags::NONE)
510    }
511}
512
513/// RAII guard for locked `CVPixelBuffer` base address
514pub struct CVPixelBufferLockGuard<'a> {
515    buffer: &'a CVPixelBuffer,
516    flags: CVPixelBufferLockFlags,
517}
518
519impl CVPixelBufferLockGuard<'_> {
520    /// Get the base address of the locked buffer
521    pub fn base_address(&self) -> *const u8 {
522        self.buffer
523            .base_address_raw()
524            .unwrap_or(std::ptr::null_mut())
525            .cast_const()
526    }
527
528    /// Get mutable base address (only valid for read-write locks)
529    ///
530    /// Returns `None` if this is a read-only lock.
531    pub fn base_address_mut(&mut self) -> Option<*mut u8> {
532        if self.flags.is_read_only() {
533            None
534        } else {
535            self.buffer.base_address_raw()
536        }
537    }
538
539    /// Get the base address of a specific plane
540    ///
541    /// For multi-planar formats like YCbCr 4:2:0:
542    /// - Plane 0: Y (luminance) data
543    /// - Plane 1: `CbCr` (chrominance) data
544    ///
545    /// Returns `None` if the plane index is out of bounds.
546    pub fn base_address_of_plane(&self, plane_index: usize) -> Option<*const u8> {
547        self.buffer
548            .base_address_of_plane_raw(plane_index)
549            .map(<*mut u8>::cast_const)
550    }
551
552    /// Get the mutable base address of a specific plane
553    ///
554    /// Returns `None` if this is a read-only lock or the plane index is out of bounds.
555    pub fn base_address_of_plane_mut(&mut self, plane_index: usize) -> Option<*mut u8> {
556        if self.flags.is_read_only() {
557            return None;
558        }
559        self.buffer.base_address_of_plane_raw(plane_index)
560    }
561
562    /// Get the width of the buffer
563    pub fn width(&self) -> usize {
564        self.buffer.width()
565    }
566
567    /// Get the height of the buffer
568    pub fn height(&self) -> usize {
569        self.buffer.height()
570    }
571
572    /// Get bytes per row
573    pub fn bytes_per_row(&self) -> usize {
574        self.buffer.bytes_per_row()
575    }
576
577    /// Get the data size in bytes
578    ///
579    /// This provides API parity with `IOSurfaceLockGuard::data_size()`.
580    pub fn data_size(&self) -> usize {
581        self.buffer.data_size()
582    }
583
584    /// Get the number of planes
585    pub fn plane_count(&self) -> usize {
586        self.buffer.plane_count()
587    }
588
589    /// Get the width of a specific plane
590    pub fn width_of_plane(&self, plane_index: usize) -> usize {
591        self.buffer.width_of_plane(plane_index)
592    }
593
594    /// Get the height of a specific plane
595    pub fn height_of_plane(&self, plane_index: usize) -> usize {
596        self.buffer.height_of_plane(plane_index)
597    }
598
599    /// Get the bytes per row of a specific plane
600    pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
601        self.buffer.bytes_per_row_of_plane(plane_index)
602    }
603
604    /// Get data as a byte slice
605    ///
606    /// The lock guard ensures the buffer is locked for the lifetime of the slice.
607    pub fn as_slice(&self) -> &[u8] {
608        let ptr = self.base_address();
609        let len = self.buffer.height() * self.buffer.bytes_per_row();
610        if ptr.is_null() || len == 0 {
611            &[]
612        } else {
613            unsafe { std::slice::from_raw_parts(ptr, len) }
614        }
615    }
616
617    /// Get data as a mutable byte slice (only valid for read-write locks)
618    ///
619    /// Returns `None` if this is a read-only lock.
620    pub fn as_slice_mut(&mut self) -> Option<&mut [u8]> {
621        let ptr = self.base_address_mut()?;
622        let len = self.buffer.height() * self.buffer.bytes_per_row();
623        if ptr.is_null() || len == 0 {
624            Some(&mut [])
625        } else {
626            Some(unsafe { std::slice::from_raw_parts_mut(ptr, len) })
627        }
628    }
629
630    /// Get a slice of plane data
631    ///
632    /// Returns the data for a specific plane as a byte slice.
633    ///
634    /// Returns `None` if the plane index is out of bounds.
635    pub fn plane_data(&self, plane_index: usize) -> Option<&[u8]> {
636        let base = self.base_address_of_plane(plane_index)?;
637        let height = self.buffer.height_of_plane(plane_index);
638        let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
639        Some(unsafe { std::slice::from_raw_parts(base, height * bytes_per_row) })
640    }
641
642    /// Get a specific row from a plane as a slice
643    ///
644    /// Returns `None` if the plane or row index is out of bounds.
645    pub fn plane_row(&self, plane_index: usize, row_index: usize) -> Option<&[u8]> {
646        if !self.buffer.is_planar() || plane_index >= self.buffer.plane_count() {
647            return None;
648        }
649        let height = self.buffer.height_of_plane(plane_index);
650        if row_index >= height {
651            return None;
652        }
653        let base = self.base_address_of_plane(plane_index)?;
654        let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
655        Some(unsafe {
656            std::slice::from_raw_parts(base.add(row_index * bytes_per_row), bytes_per_row)
657        })
658    }
659
660    /// Get a specific row as a slice
661    ///
662    /// Returns `None` if the row index is out of bounds.
663    pub fn row(&self, row_index: usize) -> Option<&[u8]> {
664        if row_index >= self.height() {
665            return None;
666        }
667        let ptr = self.base_address();
668        if ptr.is_null() {
669            return None;
670        }
671        unsafe {
672            let row_ptr = ptr.add(row_index * self.bytes_per_row());
673            Some(std::slice::from_raw_parts(row_ptr, self.bytes_per_row()))
674        }
675    }
676
677    /// Access buffer with a standard `std::io::Cursor`
678    ///
679    /// Returns a cursor over the buffer data that implements `Read` and `Seek`.
680    ///
681    /// # Examples
682    ///
683    /// ```no_run
684    /// use screencapturekit::cv::{CVPixelBuffer, CVPixelBufferLockFlags};
685    /// use std::io::{Read, Seek, SeekFrom};
686    ///
687    /// fn read_buffer(buffer: &CVPixelBuffer) {
688    ///     let guard = buffer.lock(CVPixelBufferLockFlags::READ_ONLY).unwrap();
689    ///     let mut cursor = guard.cursor();
690    ///
691    ///     // Read first 4 bytes
692    ///     let mut pixel = [0u8; 4];
693    ///     cursor.read_exact(&mut pixel).unwrap();
694    ///
695    ///     // Seek to row 10
696    ///     let offset = 10 * guard.bytes_per_row();
697    ///     cursor.seek(SeekFrom::Start(offset as u64)).unwrap();
698    /// }
699    /// ```
700    pub fn cursor(&self) -> io::Cursor<&[u8]> {
701        io::Cursor::new(self.as_slice())
702    }
703
704    /// Get raw pointer to buffer data
705    pub fn as_ptr(&self) -> *const u8 {
706        self.base_address()
707    }
708
709    /// Get mutable raw pointer to buffer data (only valid for read-write locks)
710    ///
711    /// Returns `None` if this is a read-only lock.
712    pub fn as_mut_ptr(&mut self) -> Option<*mut u8> {
713        self.base_address_mut()
714    }
715
716    /// Check if this is a read-only lock
717    pub const fn is_read_only(&self) -> bool {
718        self.flags.is_read_only()
719    }
720
721    /// Get the lock options
722    pub const fn options(&self) -> CVPixelBufferLockFlags {
723        self.flags
724    }
725
726    /// Get the pixel format
727    pub fn pixel_format(&self) -> u32 {
728        self.buffer.pixel_format()
729    }
730}
731
732impl Drop for CVPixelBufferLockGuard<'_> {
733    fn drop(&mut self) {
734        let _ = self.buffer.unlock_raw(self.flags);
735    }
736}
737
738impl std::fmt::Debug for CVPixelBufferLockGuard<'_> {
739    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
740        f.debug_struct("CVPixelBufferLockGuard")
741            .field("flags", &self.flags)
742            .field("buffer_size", &(self.buffer.width(), self.buffer.height()))
743            .finish()
744    }
745}
746
747impl std::ops::Deref for CVPixelBufferLockGuard<'_> {
748    type Target = [u8];
749
750    fn deref(&self) -> &Self::Target {
751        self.as_slice()
752    }
753}
754
755impl Clone for CVPixelBuffer {
756    fn clone(&self) -> Self {
757        unsafe {
758            let ptr = ffi::cv_pixel_buffer_retain(self.0);
759            Self(ptr)
760        }
761    }
762}
763
764impl Drop for CVPixelBuffer {
765    fn drop(&mut self) {
766        unsafe {
767            ffi::cv_pixel_buffer_release(self.0);
768        }
769    }
770}
771
772unsafe impl Send for CVPixelBuffer {}
773unsafe impl Sync for CVPixelBuffer {}
774
775impl fmt::Display for CVPixelBuffer {
776    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777        write!(
778            f,
779            "CVPixelBuffer({}x{}, format: 0x{:08X})",
780            self.width(),
781            self.height(),
782            self.pixel_format()
783        )
784    }
785}
786
787/// Opaque handle to `CVPixelBufferPool`
788#[repr(transparent)]
789#[derive(Debug)]
790pub struct CVPixelBufferPool(*mut std::ffi::c_void);
791
792impl PartialEq for CVPixelBufferPool {
793    fn eq(&self, other: &Self) -> bool {
794        self.0 == other.0
795    }
796}
797
798impl Eq for CVPixelBufferPool {}
799
800impl std::hash::Hash for CVPixelBufferPool {
801    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
802        unsafe {
803            let hash_value = ffi::cv_pixel_buffer_pool_hash(self.0);
804            hash_value.hash(state);
805        }
806    }
807}
808
809impl CVPixelBufferPool {
810    pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
811        if ptr.is_null() {
812            None
813        } else {
814            Some(Self(ptr))
815        }
816    }
817
818    /// # Safety
819    /// The caller must ensure the pointer is a valid `CVPixelBufferPool` pointer.
820    pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
821        Self(ptr)
822    }
823
824    pub fn as_ptr(&self) -> *mut std::ffi::c_void {
825        self.0
826    }
827
828    /// Create a new pixel buffer pool
829    ///
830    /// # Arguments
831    ///
832    /// * `width` - Width of pixel buffers in the pool
833    /// * `height` - Height of pixel buffers in the pool
834    /// * `pixel_format` - Pixel format type
835    /// * `max_buffers` - Maximum number of buffers in the pool (0 for unlimited)
836    ///
837    /// # Errors
838    ///
839    /// Returns a Core Video error code if the pool creation fails.
840    pub fn create(
841        width: usize,
842        height: usize,
843        pixel_format: u32,
844        max_buffers: usize,
845    ) -> Result<Self, i32> {
846        unsafe {
847            let mut pool_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
848            let status = ffi::cv_pixel_buffer_pool_create(
849                width,
850                height,
851                pixel_format,
852                max_buffers,
853                &mut pool_ptr,
854            );
855
856            if status == 0 && !pool_ptr.is_null() {
857                Ok(Self(pool_ptr))
858            } else {
859                Err(status)
860            }
861        }
862    }
863
864    /// Create a pixel buffer from the pool
865    ///
866    /// # Errors
867    ///
868    /// Returns a Core Video error code if the buffer creation fails.
869    pub fn create_pixel_buffer(&self) -> Result<CVPixelBuffer, i32> {
870        unsafe {
871            let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
872            let status =
873                ffi::cv_pixel_buffer_pool_create_pixel_buffer(self.0, &mut pixel_buffer_ptr);
874
875            if status == 0 && !pixel_buffer_ptr.is_null() {
876                Ok(CVPixelBuffer(pixel_buffer_ptr))
877            } else {
878                Err(status)
879            }
880        }
881    }
882
883    /// Flush the pixel buffer pool
884    ///
885    /// Releases all available pixel buffers in the pool
886    pub fn flush(&self) {
887        unsafe {
888            ffi::cv_pixel_buffer_pool_flush(self.0);
889        }
890    }
891
892    /// Get the Core Foundation type ID for `CVPixelBufferPool`
893    pub fn type_id() -> usize {
894        unsafe { ffi::cv_pixel_buffer_pool_get_type_id() }
895    }
896
897    /// Create a pixel buffer from the pool with auxiliary attributes
898    ///
899    /// This allows specifying additional attributes for the created buffer
900    ///
901    /// # Errors
902    ///
903    /// Returns a Core Video error code if the buffer creation fails.
904    pub fn create_pixel_buffer_with_aux_attributes(
905        &self,
906        aux_attributes: Option<&std::collections::HashMap<String, u32>>,
907    ) -> Result<CVPixelBuffer, i32> {
908        // For now, ignore aux_attributes since we don't have a way to pass them through
909        // In a full implementation, this would convert the HashMap to a CFDictionary
910        let _ = aux_attributes;
911        self.create_pixel_buffer()
912    }
913
914    /// Try to create a pixel buffer from the pool without blocking
915    ///
916    /// Returns None if no buffers are available
917    pub fn try_create_pixel_buffer(&self) -> Option<CVPixelBuffer> {
918        self.create_pixel_buffer().ok()
919    }
920
921    /// Flush the pool with specific options
922    ///
923    /// Releases buffers based on the provided flags
924    pub fn flush_with_options(&self, _flags: u32) {
925        // For now, just call regular flush
926        // In a full implementation, this would pass flags to the Swift side
927        self.flush();
928    }
929
930    /// Check if the pool is empty (no available buffers)
931    ///
932    /// Note: This is an approximation based on whether we can create a buffer
933    pub fn is_empty(&self) -> bool {
934        self.try_create_pixel_buffer().is_none()
935    }
936
937    /// Get the pool attributes
938    ///
939    /// Returns the raw pointer to the `CFDictionary` containing pool attributes
940    pub fn attributes(&self) -> Option<*const std::ffi::c_void> {
941        unsafe {
942            let ptr = ffi::cv_pixel_buffer_pool_get_attributes(self.0);
943            if ptr.is_null() {
944                None
945            } else {
946                Some(ptr)
947            }
948        }
949    }
950
951    /// Get the pixel buffer attributes
952    ///
953    /// Returns the raw pointer to the `CFDictionary` containing pixel buffer attributes
954    pub fn pixel_buffer_attributes(&self) -> Option<*const std::ffi::c_void> {
955        unsafe {
956            let ptr = ffi::cv_pixel_buffer_pool_get_pixel_buffer_attributes(self.0);
957            if ptr.is_null() {
958                None
959            } else {
960                Some(ptr)
961            }
962        }
963    }
964}
965
966impl Clone for CVPixelBufferPool {
967    fn clone(&self) -> Self {
968        unsafe {
969            let ptr = ffi::cv_pixel_buffer_pool_retain(self.0);
970            Self(ptr)
971        }
972    }
973}
974
975impl Drop for CVPixelBufferPool {
976    fn drop(&mut self) {
977        unsafe {
978            ffi::cv_pixel_buffer_pool_release(self.0);
979        }
980    }
981}
982
983unsafe impl Send for CVPixelBufferPool {}
984unsafe impl Sync for CVPixelBufferPool {}
985
986impl fmt::Display for CVPixelBufferPool {
987    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988        write!(f, "CVPixelBufferPool")
989    }
990}
991
992/// Extension trait for `io::Cursor` to add pixel buffer specific operations
993pub trait PixelBufferCursorExt {
994    /// Seek to a specific pixel coordinate (x, y)
995    ///
996    /// Assumes 4 bytes per pixel (BGRA format).
997    ///
998    /// # Errors
999    ///
1000    /// Returns an I/O error if the seek operation fails.
1001    fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64>;
1002
1003    /// Read a single pixel (4 bytes: BGRA)
1004    ///
1005    /// # Errors
1006    ///
1007    /// Returns an I/O error if the read operation fails.
1008    fn read_pixel(&mut self) -> io::Result<[u8; 4]>;
1009}
1010
1011impl<T: AsRef<[u8]>> PixelBufferCursorExt for io::Cursor<T> {
1012    fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64> {
1013        let pos = y * bytes_per_row + x * 4; // 4 bytes per pixel (BGRA)
1014        self.seek(SeekFrom::Start(pos as u64))
1015    }
1016
1017    fn read_pixel(&mut self) -> io::Result<[u8; 4]> {
1018        let mut pixel = [0u8; 4];
1019        self.read_exact(&mut pixel)?;
1020        Ok(pixel)
1021    }
1022}