Skip to main content

apple_cf/cv/
buffer.rs

1//! Core Video buffer/image-buffer wrappers.
2//!
3#![allow(clippy::missing_errors_doc)]
4
5//! ```rust
6//! use apple_cf::cf::{CFString, CFType};
7//! use apple_cf::cv::{CVAttachmentMode, CVBuffer, CVImageBuffer, CVPixelBuffer};
8//!
9//! let pixel_buffer = CVPixelBuffer::create(8, 8, 0x4247_5241).expect("pixel buffer");
10//! let buffer = CVBuffer::from_pixel_buffer(&pixel_buffer).expect("buffer");
11//! let key = CFString::new("com.doomfish.apple-cf.example");
12//! let value = CFString::new("value");
13//! buffer.set_attachment(&key, &value, CVAttachmentMode::ShouldPropagate);
14//! assert!(buffer.attachment(&key).is_some());
15//!
16//! let image = CVImageBuffer::from_pixel_buffer(&pixel_buffer).expect("image buffer");
17//! assert_eq!(image.encoded_size().width, 8.0);
18//! ```
19
20use super::CVPixelBuffer;
21use crate::cf::{AsCFType, CFDictionary, CFString, CFType};
22use std::ffi::c_void;
23use std::fmt;
24
25#[link(name = "CoreVideo", kind = "framework")]
26extern "C" {
27    fn CVBufferRetain(buffer: *mut c_void) -> *mut c_void;
28    fn CVBufferRelease(buffer: *mut c_void);
29    fn CVBufferSetAttachment(buffer: *mut c_void, key: *mut c_void, value: *mut c_void, mode: u32);
30    fn CVBufferCopyAttachment(
31        buffer: *mut c_void,
32        key: *mut c_void,
33        attachment_mode: *mut u32,
34    ) -> *mut c_void;
35    fn CVBufferCopyAttachments(buffer: *mut c_void, attachment_mode: u32) -> *mut c_void;
36    fn CVBufferRemoveAllAttachments(buffer: *mut c_void);
37
38    fn CVImageBufferGetEncodedSize(image_buffer: *mut c_void) -> CVImageSize;
39    fn CVImageBufferGetDisplaySize(image_buffer: *mut c_void) -> CVImageSize;
40    fn CVImageBufferGetCleanRect(image_buffer: *mut c_void) -> CVImageRect;
41}
42
43/// Attachment propagation mode.
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45#[repr(u32)]
46pub enum CVAttachmentMode {
47    ShouldNotPropagate = 0,
48    ShouldPropagate = 1,
49}
50
51/// Size returned by `CVImageBuffer` accessors.
52#[repr(C)]
53#[derive(Debug, Clone, Copy, PartialEq)]
54pub struct CVImageSize {
55    pub width: f64,
56    pub height: f64,
57}
58
59/// Rectangle returned by `CVImageBufferGetCleanRect`.
60#[repr(C)]
61#[derive(Debug, Clone, Copy, PartialEq)]
62pub struct CVImageRect {
63    pub x: f64,
64    pub y: f64,
65    pub width: f64,
66    pub height: f64,
67}
68
69/// Generic `CVBufferRef` wrapper.
70pub struct CVBuffer(*mut c_void);
71
72impl CVBuffer {
73    /// Adopts a +1 retained `CVBufferRef` and returns `None` for null.
74    ///
75    /// # Safety
76    ///
77    /// A non-null `ptr` must be a live `CVBufferRef` of the exact type carrying
78    /// one retain transferred to this wrapper. The caller must not release or
79    /// separately adopt that transferred retain.
80    #[must_use]
81    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
82        if ptr.is_null() {
83            None
84        } else {
85            Some(Self(ptr))
86        }
87    }
88
89    /// Retains a +0 borrowed `CVBufferRef` and returns an owned wrapper.
90    ///
91    /// # Safety
92    ///
93    /// A non-null `ptr` must be a live `CVBufferRef` of the exact type for the
94    /// duration of the retain call.
95    #[must_use]
96    pub unsafe fn from_raw_borrowed(ptr: *mut c_void) -> Option<Self> {
97        if ptr.is_null() {
98            None
99        } else {
100            let retained = unsafe { CVBufferRetain(ptr) };
101            unsafe { Self::from_raw(retained) }
102        }
103    }
104
105    /// Wrap a pixel buffer as a generic `CVBuffer`.
106    #[must_use]
107    pub fn from_pixel_buffer(pixel_buffer: &CVPixelBuffer) -> Option<Self> {
108        unsafe { Self::from_raw_borrowed(pixel_buffer.as_ptr()) }
109    }
110
111    /// Borrow the raw +0 `CVBufferRef` while `self` remains alive.
112    #[must_use]
113    pub const fn as_ptr(&self) -> *mut c_void {
114        self.0
115    }
116
117    /// Attach a Core Foundation value to the buffer.
118    pub fn set_attachment(&self, key: &CFString, value: &dyn AsCFType, mode: CVAttachmentMode) {
119        unsafe { CVBufferSetAttachment(self.0, key.as_ptr(), value.as_ptr(), mode as u32) };
120    }
121
122    /// Copy an attachment value for `key`.
123    #[must_use]
124    pub fn attachment(&self, key: &CFString) -> Option<CFType> {
125        let mut attachment_mode = 0_u32;
126        let ptr = unsafe { CVBufferCopyAttachment(self.0, key.as_ptr(), &mut attachment_mode) };
127        unsafe { CFType::from_raw(ptr) }
128    }
129
130    /// Copy all attachments for the requested propagation mode.
131    #[must_use]
132    pub fn attachments(&self, mode: CVAttachmentMode) -> Option<CFDictionary> {
133        let ptr = unsafe { CVBufferCopyAttachments(self.0, mode as u32) };
134        unsafe { CFDictionary::from_raw(ptr) }
135    }
136
137    /// Remove all attachments.
138    pub fn remove_all_attachments(&self) {
139        unsafe { CVBufferRemoveAllAttachments(self.0) };
140    }
141}
142
143crate::utils::retained::cf_retained!(
144    CVBuffer,
145    retain = CVBufferRetain,
146    release = CVBufferRelease,
147    drop = unchecked,
148);
149
150impl PartialEq for CVBuffer {
151    fn eq(&self, other: &Self) -> bool {
152        self.0 == other.0
153    }
154}
155
156impl Eq for CVBuffer {}
157
158impl std::hash::Hash for CVBuffer {
159    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
160        self.0.hash(state);
161    }
162}
163
164impl fmt::Debug for CVBuffer {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        f.debug_struct("CVBuffer").field("ptr", &self.0).finish()
167    }
168}
169
170/// Generic `CVImageBufferRef` wrapper.
171pub struct CVImageBuffer(*mut c_void);
172
173impl CVImageBuffer {
174    /// Adopts a +1 retained `CVImageBufferRef` and returns `None` for null.
175    ///
176    /// # Safety
177    ///
178    /// A non-null `ptr` must be a live `CVImageBufferRef` of the exact type
179    /// carrying one retain transferred to this wrapper. The caller must not
180    /// release or separately adopt that transferred retain.
181    #[must_use]
182    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
183        if ptr.is_null() {
184            None
185        } else {
186            Some(Self(ptr))
187        }
188    }
189
190    /// Retains a +0 borrowed `CVImageBufferRef` and returns an owned wrapper.
191    ///
192    /// # Safety
193    ///
194    /// A non-null `ptr` must be a live `CVImageBufferRef` of the exact type for
195    /// the duration of the retain call.
196    #[must_use]
197    pub unsafe fn from_raw_borrowed(ptr: *mut c_void) -> Option<Self> {
198        if ptr.is_null() {
199            None
200        } else {
201            let retained = unsafe { CVBufferRetain(ptr) };
202            unsafe { Self::from_raw(retained) }
203        }
204    }
205
206    /// Wrap a pixel buffer as a generic image buffer.
207    #[must_use]
208    pub fn from_pixel_buffer(pixel_buffer: &CVPixelBuffer) -> Option<Self> {
209        unsafe { Self::from_raw_borrowed(pixel_buffer.as_ptr()) }
210    }
211
212    /// Borrow the raw +0 `CVImageBufferRef` while `self` remains alive.
213    #[must_use]
214    pub const fn as_ptr(&self) -> *mut c_void {
215        self.0
216    }
217
218    /// Encoded image size.
219    #[must_use]
220    pub fn encoded_size(&self) -> CVImageSize {
221        unsafe { CVImageBufferGetEncodedSize(self.0) }
222    }
223
224    /// Display size.
225    #[must_use]
226    pub fn display_size(&self) -> CVImageSize {
227        unsafe { CVImageBufferGetDisplaySize(self.0) }
228    }
229
230    /// Clean aperture rectangle.
231    #[must_use]
232    pub fn clean_rect(&self) -> CVImageRect {
233        unsafe { CVImageBufferGetCleanRect(self.0) }
234    }
235}
236
237crate::utils::retained::cf_retained!(
238    CVImageBuffer,
239    retain = CVBufferRetain,
240    release = CVBufferRelease,
241    drop = unchecked,
242);
243
244impl PartialEq for CVImageBuffer {
245    fn eq(&self, other: &Self) -> bool {
246        self.0 == other.0
247    }
248}
249
250impl Eq for CVImageBuffer {}
251
252impl std::hash::Hash for CVImageBuffer {
253    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
254        self.0.hash(state);
255    }
256}
257
258impl fmt::Debug for CVImageBuffer {
259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260        f.debug_struct("CVImageBuffer")
261            .field("ptr", &self.0)
262            .field("encoded_size", &self.encoded_size())
263            .finish()
264    }
265}