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