use super::ffi;
use crate::cm::IOSurface;
use std::fmt;
use std::io::{self, Read, Seek, SeekFrom};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct CVPixelBufferLockFlags(u32);
impl CVPixelBufferLockFlags {
pub const NONE: Self = Self(0);
pub const READ_ONLY: Self = Self(0x0000_0001);
#[must_use]
pub const fn from_bits(bits: u32) -> Self {
Self(bits)
}
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
#[must_use]
pub const fn is_read_only(self) -> bool {
(self.0 & Self::READ_ONLY.0) != 0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl From<CVPixelBufferLockFlags> for u32 {
fn from(flags: CVPixelBufferLockFlags) -> Self {
flags.0
}
}
#[derive(Debug)]
pub struct CVPixelBuffer(*mut std::ffi::c_void);
impl PartialEq for CVPixelBuffer {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for CVPixelBuffer {}
impl std::hash::Hash for CVPixelBuffer {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
let hash_value = ffi::cv_pixel_buffer_hash(self.0);
hash_value.hash(state);
}
}
}
impl CVPixelBuffer {
pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
Self(ptr)
}
pub fn as_ptr(&self) -> *mut std::ffi::c_void {
self.0
}
pub fn create(width: usize, height: usize, pixel_format: u32) -> Result<Self, i32> {
unsafe {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status =
ffi::cv_pixel_buffer_create(width, height, pixel_format, &mut pixel_buffer_ptr);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
}
pub unsafe fn create_with_bytes(
width: usize,
height: usize,
pixel_format: u32,
base_address: *mut std::ffi::c_void,
bytes_per_row: usize,
) -> Result<Self, i32> {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_create_with_bytes(
width,
height,
pixel_format,
base_address,
bytes_per_row,
&mut pixel_buffer_ptr,
);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
pub fn fill_extended_pixels(&self) -> Result<(), i32> {
unsafe {
let status = ffi::cv_pixel_buffer_fill_extended_pixels(self.0);
if status == 0 {
Ok(())
} else {
Err(status)
}
}
}
pub unsafe fn create_with_planar_bytes(
width: usize,
height: usize,
pixel_format: u32,
plane_base_addresses: &[*mut std::ffi::c_void],
plane_widths: &[usize],
plane_heights: &[usize],
plane_bytes_per_row: &[usize],
) -> Result<Self, i32> {
if plane_base_addresses.len() != plane_widths.len()
|| plane_widths.len() != plane_heights.len()
|| plane_heights.len() != plane_bytes_per_row.len()
{
return Err(-50); }
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_create_with_planar_bytes(
width,
height,
pixel_format,
plane_base_addresses.len(),
plane_base_addresses.as_ptr(),
plane_widths.as_ptr(),
plane_heights.as_ptr(),
plane_bytes_per_row.as_ptr(),
&mut pixel_buffer_ptr,
);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
pub fn create_with_io_surface(surface: &IOSurface) -> Result<Self, i32> {
unsafe {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_create_with_io_surface(
surface.as_ptr(),
&mut pixel_buffer_ptr,
);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(Self(pixel_buffer_ptr))
} else {
Err(status)
}
}
}
pub fn type_id() -> usize {
unsafe { ffi::cv_pixel_buffer_get_type_id() }
}
pub fn data_size(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_data_size(self.0) }
}
pub fn is_planar(&self) -> bool {
unsafe { ffi::cv_pixel_buffer_is_planar(self.0) }
}
pub fn plane_count(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_plane_count(self.0) }
}
pub fn width_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::cv_pixel_buffer_get_width_of_plane(self.0, plane_index) }
}
pub fn height_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::cv_pixel_buffer_get_height_of_plane(self.0, plane_index) }
}
fn base_address_of_plane_raw(&self, plane_index: usize) -> Option<*mut u8> {
unsafe {
let ptr = ffi::cv_pixel_buffer_get_base_address_of_plane(self.0, plane_index);
if ptr.is_null() {
None
} else {
Some(ptr.cast::<u8>())
}
}
}
pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::cv_pixel_buffer_get_bytes_per_row_of_plane(self.0, plane_index) }
}
pub fn extended_pixels(&self) -> (usize, usize, usize, usize) {
unsafe {
let mut left: usize = 0;
let mut right: usize = 0;
let mut top: usize = 0;
let mut bottom: usize = 0;
ffi::cv_pixel_buffer_get_extended_pixels(
self.0,
&mut left,
&mut right,
&mut top,
&mut bottom,
);
(left, right, top, bottom)
}
}
pub fn is_backed_by_io_surface(&self) -> bool {
self.io_surface().is_some()
}
pub fn width(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_width(self.0) }
}
pub fn height(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_height(self.0) }
}
pub fn pixel_format(&self) -> u32 {
unsafe { ffi::cv_pixel_buffer_get_pixel_format_type(self.0) }
}
pub fn bytes_per_row(&self) -> usize {
unsafe { ffi::cv_pixel_buffer_get_bytes_per_row(self.0) }
}
pub fn lock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
unsafe {
let result = ffi::cv_pixel_buffer_lock_base_address(self.0, flags.as_u32());
if result == 0 {
Ok(())
} else {
Err(result)
}
}
}
pub fn unlock_raw(&self, flags: CVPixelBufferLockFlags) -> Result<(), i32> {
unsafe {
let result = ffi::cv_pixel_buffer_unlock_base_address(self.0, flags.as_u32());
if result == 0 {
Ok(())
} else {
Err(result)
}
}
}
fn base_address_raw(&self) -> Option<*mut u8> {
unsafe {
let ptr = ffi::cv_pixel_buffer_get_base_address(self.0);
if ptr.is_null() {
None
} else {
Some(ptr.cast::<u8>())
}
}
}
pub fn io_surface(&self) -> Option<IOSurface> {
unsafe {
let ptr = ffi::cv_pixel_buffer_get_io_surface(self.0);
IOSurface::from_raw(ptr)
}
}
pub fn lock(&self, flags: CVPixelBufferLockFlags) -> Result<CVPixelBufferLockGuard<'_>, i32> {
self.lock_raw(flags)?;
Ok(CVPixelBufferLockGuard {
buffer: self,
flags,
})
}
pub fn lock_read_only(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
self.lock(CVPixelBufferLockFlags::READ_ONLY)
}
pub fn lock_read_write(&self) -> Result<CVPixelBufferLockGuard<'_>, i32> {
self.lock(CVPixelBufferLockFlags::NONE)
}
}
pub struct CVPixelBufferLockGuard<'a> {
buffer: &'a CVPixelBuffer,
flags: CVPixelBufferLockFlags,
}
impl CVPixelBufferLockGuard<'_> {
pub fn base_address(&self) -> *const u8 {
self.buffer
.base_address_raw()
.unwrap_or(std::ptr::null_mut())
.cast_const()
}
pub fn base_address_mut(&mut self) -> Option<*mut u8> {
if self.flags.is_read_only() {
None
} else {
self.buffer.base_address_raw()
}
}
pub fn base_address_of_plane(&self, plane_index: usize) -> Option<*const u8> {
self.buffer
.base_address_of_plane_raw(plane_index)
.map(<*mut u8>::cast_const)
}
pub fn base_address_of_plane_mut(&mut self, plane_index: usize) -> Option<*mut u8> {
if self.flags.is_read_only() {
return None;
}
self.buffer.base_address_of_plane_raw(plane_index)
}
pub fn width(&self) -> usize {
self.buffer.width()
}
pub fn height(&self) -> usize {
self.buffer.height()
}
pub fn bytes_per_row(&self) -> usize {
self.buffer.bytes_per_row()
}
pub fn data_size(&self) -> usize {
self.buffer.data_size()
}
pub fn plane_count(&self) -> usize {
self.buffer.plane_count()
}
pub fn width_of_plane(&self, plane_index: usize) -> usize {
self.buffer.width_of_plane(plane_index)
}
pub fn height_of_plane(&self, plane_index: usize) -> usize {
self.buffer.height_of_plane(plane_index)
}
pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
self.buffer.bytes_per_row_of_plane(plane_index)
}
pub fn as_slice(&self) -> &[u8] {
let ptr = self.base_address();
let len = self.buffer.height() * self.buffer.bytes_per_row();
if ptr.is_null() || len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(ptr, len) }
}
}
pub fn as_slice_mut(&mut self) -> Option<&mut [u8]> {
let ptr = self.base_address_mut()?;
let len = self.buffer.height() * self.buffer.bytes_per_row();
if ptr.is_null() || len == 0 {
Some(&mut [])
} else {
Some(unsafe { std::slice::from_raw_parts_mut(ptr, len) })
}
}
pub fn plane_data(&self, plane_index: usize) -> Option<&[u8]> {
let base = self.base_address_of_plane(plane_index)?;
let height = self.buffer.height_of_plane(plane_index);
let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
Some(unsafe { std::slice::from_raw_parts(base, height * bytes_per_row) })
}
pub fn plane_row(&self, plane_index: usize, row_index: usize) -> Option<&[u8]> {
if !self.buffer.is_planar() || plane_index >= self.buffer.plane_count() {
return None;
}
let height = self.buffer.height_of_plane(plane_index);
if row_index >= height {
return None;
}
let base = self.base_address_of_plane(plane_index)?;
let bytes_per_row = self.buffer.bytes_per_row_of_plane(plane_index);
Some(unsafe {
std::slice::from_raw_parts(base.add(row_index * bytes_per_row), bytes_per_row)
})
}
pub fn row(&self, row_index: usize) -> Option<&[u8]> {
if row_index >= self.height() {
return None;
}
let ptr = self.base_address();
if ptr.is_null() {
return None;
}
unsafe {
let row_ptr = ptr.add(row_index * self.bytes_per_row());
Some(std::slice::from_raw_parts(row_ptr, self.bytes_per_row()))
}
}
pub fn cursor(&self) -> io::Cursor<&[u8]> {
io::Cursor::new(self.as_slice())
}
pub fn as_ptr(&self) -> *const u8 {
self.base_address()
}
pub fn as_mut_ptr(&mut self) -> Option<*mut u8> {
self.base_address_mut()
}
pub const fn is_read_only(&self) -> bool {
self.flags.is_read_only()
}
pub const fn options(&self) -> CVPixelBufferLockFlags {
self.flags
}
pub fn pixel_format(&self) -> u32 {
self.buffer.pixel_format()
}
}
impl Drop for CVPixelBufferLockGuard<'_> {
fn drop(&mut self) {
let _ = self.buffer.unlock_raw(self.flags);
}
}
impl std::fmt::Debug for CVPixelBufferLockGuard<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CVPixelBufferLockGuard")
.field("flags", &self.flags)
.field("buffer_size", &(self.buffer.width(), self.buffer.height()))
.finish()
}
}
impl std::ops::Deref for CVPixelBufferLockGuard<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl Clone for CVPixelBuffer {
fn clone(&self) -> Self {
unsafe {
let ptr = ffi::cv_pixel_buffer_retain(self.0);
Self(ptr)
}
}
}
impl Drop for CVPixelBuffer {
fn drop(&mut self) {
unsafe {
ffi::cv_pixel_buffer_release(self.0);
}
}
}
unsafe impl Send for CVPixelBuffer {}
unsafe impl Sync for CVPixelBuffer {}
impl fmt::Display for CVPixelBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CVPixelBuffer({}x{}, format: 0x{:08X})",
self.width(),
self.height(),
self.pixel_format()
)
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct CVPixelBufferPool(*mut std::ffi::c_void);
impl PartialEq for CVPixelBufferPool {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for CVPixelBufferPool {}
impl std::hash::Hash for CVPixelBufferPool {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
let hash_value = ffi::cv_pixel_buffer_pool_hash(self.0);
hash_value.hash(state);
}
}
}
impl CVPixelBufferPool {
pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
Self(ptr)
}
pub fn as_ptr(&self) -> *mut std::ffi::c_void {
self.0
}
pub fn create(
width: usize,
height: usize,
pixel_format: u32,
max_buffers: usize,
) -> Result<Self, i32> {
unsafe {
let mut pool_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status = ffi::cv_pixel_buffer_pool_create(
width,
height,
pixel_format,
max_buffers,
&mut pool_ptr,
);
if status == 0 && !pool_ptr.is_null() {
Ok(Self(pool_ptr))
} else {
Err(status)
}
}
}
pub fn create_pixel_buffer(&self) -> Result<CVPixelBuffer, i32> {
unsafe {
let mut pixel_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let status =
ffi::cv_pixel_buffer_pool_create_pixel_buffer(self.0, &mut pixel_buffer_ptr);
if status == 0 && !pixel_buffer_ptr.is_null() {
Ok(CVPixelBuffer(pixel_buffer_ptr))
} else {
Err(status)
}
}
}
pub fn flush(&self) {
unsafe {
ffi::cv_pixel_buffer_pool_flush(self.0);
}
}
pub fn type_id() -> usize {
unsafe { ffi::cv_pixel_buffer_pool_get_type_id() }
}
pub fn create_pixel_buffer_with_aux_attributes(
&self,
aux_attributes: Option<&std::collections::HashMap<String, u32>>,
) -> Result<CVPixelBuffer, i32> {
let _ = aux_attributes;
self.create_pixel_buffer()
}
pub fn try_create_pixel_buffer(&self) -> Option<CVPixelBuffer> {
self.create_pixel_buffer().ok()
}
pub fn flush_with_options(&self, _flags: u32) {
self.flush();
}
pub fn is_empty(&self) -> bool {
self.try_create_pixel_buffer().is_none()
}
pub fn attributes(&self) -> Option<*const std::ffi::c_void> {
unsafe {
let ptr = ffi::cv_pixel_buffer_pool_get_attributes(self.0);
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
}
pub fn pixel_buffer_attributes(&self) -> Option<*const std::ffi::c_void> {
unsafe {
let ptr = ffi::cv_pixel_buffer_pool_get_pixel_buffer_attributes(self.0);
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
}
}
impl Clone for CVPixelBufferPool {
fn clone(&self) -> Self {
unsafe {
let ptr = ffi::cv_pixel_buffer_pool_retain(self.0);
Self(ptr)
}
}
}
impl Drop for CVPixelBufferPool {
fn drop(&mut self) {
unsafe {
ffi::cv_pixel_buffer_pool_release(self.0);
}
}
}
unsafe impl Send for CVPixelBufferPool {}
unsafe impl Sync for CVPixelBufferPool {}
impl fmt::Display for CVPixelBufferPool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CVPixelBufferPool")
}
}
pub trait PixelBufferCursorExt {
fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64>;
fn read_pixel(&mut self) -> io::Result<[u8; 4]>;
}
impl<T: AsRef<[u8]>> PixelBufferCursorExt for io::Cursor<T> {
fn seek_to_pixel(&mut self, x: usize, y: usize, bytes_per_row: usize) -> io::Result<u64> {
let pos = y * bytes_per_row + x * 4; self.seek(SeekFrom::Start(pos as u64))
}
fn read_pixel(&mut self) -> io::Result<[u8; 4]> {
let mut pixel = [0u8; 4];
self.read_exact(&mut pixel)?;
Ok(pixel)
}
}