Skip to main content

apple_cf/cv/
pixel_buffer.rs

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