use super::ffi;
use std::ffi::c_void;
use std::fmt;
use std::io;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct IOSurfaceLockOptions(u32);
impl IOSurfaceLockOptions {
pub const NONE: Self = Self(0);
pub const READ_ONLY: Self = Self(0x0000_0001);
pub const AVOID_SYNC: Self = Self(0x0000_0002);
#[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 contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
#[must_use]
pub const fn is_read_only(self) -> bool {
self.contains(Self::READ_ONLY)
}
#[must_use]
pub const fn is_avoid_sync(self) -> bool {
self.contains(Self::AVOID_SYNC)
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl std::ops::BitOr for IOSurfaceLockOptions {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for IOSurfaceLockOptions {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAnd for IOSurfaceLockOptions {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl std::ops::BitAndAssign for IOSurfaceLockOptions {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl From<IOSurfaceLockOptions> for u32 {
fn from(options: IOSurfaceLockOptions) -> Self {
options.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PlaneProperties {
pub width: usize,
pub height: usize,
pub bytes_per_row: usize,
pub bytes_per_element: usize,
pub offset: usize,
pub size: usize,
}
pub struct IOSurface(*mut c_void);
impl PartialEq for IOSurface {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for IOSurface {}
impl std::hash::Hash for IOSurface {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
let hash_value = ffi::io_surface_hash(self.0);
hash_value.hash(state);
}
}
}
impl IOSurface {
#[must_use]
pub fn create(
width: usize,
height: usize,
pixel_format: u32,
bytes_per_element: usize,
) -> Option<Self> {
let mut ptr: *mut c_void = std::ptr::null_mut();
let status = unsafe {
crate::ffi::io_surface_create(width, height, pixel_format, bytes_per_element, &mut ptr)
};
if status == 0 && !ptr.is_null() {
Some(Self(ptr))
} else {
None
}
}
#[must_use]
#[allow(clippy::option_if_let_else)]
pub fn create_with_properties(
width: usize,
height: usize,
pixel_format: u32,
bytes_per_element: usize,
bytes_per_row: usize,
alloc_size: usize,
planes: Option<&[PlaneProperties]>,
) -> Option<Self> {
let mut ptr: *mut c_void = std::ptr::null_mut();
let (
plane_count,
plane_widths,
plane_heights,
plane_row_bytes,
plane_elem_bytes,
plane_offsets,
plane_sizes,
) = if let Some(p) = planes {
let widths: Vec<usize> = p.iter().map(|x| x.width).collect();
let heights: Vec<usize> = p.iter().map(|x| x.height).collect();
let row_bytes: Vec<usize> = p.iter().map(|x| x.bytes_per_row).collect();
let elem_bytes: Vec<usize> = p.iter().map(|x| x.bytes_per_element).collect();
let offsets: Vec<usize> = p.iter().map(|x| x.offset).collect();
let sizes: Vec<usize> = p.iter().map(|x| x.size).collect();
(
p.len(),
widths,
heights,
row_bytes,
elem_bytes,
offsets,
sizes,
)
} else {
(0, vec![], vec![], vec![], vec![], vec![], vec![])
};
let status = unsafe {
crate::ffi::io_surface_create_with_properties(
width,
height,
pixel_format,
bytes_per_element,
bytes_per_row,
alloc_size,
plane_count,
if plane_count > 0 {
plane_widths.as_ptr()
} else {
std::ptr::null()
},
if plane_count > 0 {
plane_heights.as_ptr()
} else {
std::ptr::null()
},
if plane_count > 0 {
plane_row_bytes.as_ptr()
} else {
std::ptr::null()
},
if plane_count > 0 {
plane_elem_bytes.as_ptr()
} else {
std::ptr::null()
},
if plane_count > 0 {
plane_offsets.as_ptr()
} else {
std::ptr::null()
},
if plane_count > 0 {
plane_sizes.as_ptr()
} else {
std::ptr::null()
},
&mut ptr,
)
};
if status == 0 && !ptr.is_null() {
Some(Self(ptr))
} else {
None
}
}
pub fn from_raw(ptr: *mut c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
pub unsafe fn from_ptr(ptr: *mut c_void) -> Self {
Self(ptr)
}
pub fn as_ptr(&self) -> *mut c_void {
self.0
}
pub fn width(&self) -> usize {
unsafe { ffi::io_surface_get_width(self.0) }
}
pub fn height(&self) -> usize {
unsafe { ffi::io_surface_get_height(self.0) }
}
pub fn bytes_per_row(&self) -> usize {
unsafe { ffi::io_surface_get_bytes_per_row(self.0) }
}
pub fn alloc_size(&self) -> usize {
unsafe { ffi::io_surface_get_alloc_size(self.0) }
}
pub fn data_size(&self) -> usize {
self.alloc_size()
}
pub fn pixel_format(&self) -> u32 {
unsafe { ffi::io_surface_get_pixel_format(self.0) }
}
pub fn id(&self) -> u32 {
unsafe { ffi::io_surface_get_id(self.0) }
}
pub fn seed(&self) -> u32 {
unsafe { ffi::io_surface_get_seed(self.0) }
}
pub fn plane_count(&self) -> usize {
unsafe { ffi::io_surface_get_plane_count(self.0) }
}
pub fn width_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::io_surface_get_width_of_plane(self.0, plane_index) }
}
pub fn height_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::io_surface_get_height_of_plane(self.0, plane_index) }
}
pub fn bytes_per_row_of_plane(&self, plane_index: usize) -> usize {
unsafe { ffi::io_surface_get_bytes_per_row_of_plane(self.0, plane_index) }
}
pub fn bytes_per_element(&self) -> usize {
unsafe { ffi::io_surface_get_bytes_per_element(self.0) }
}
pub fn element_width(&self) -> usize {
unsafe { ffi::io_surface_get_element_width(self.0) }
}
pub fn element_height(&self) -> usize {
unsafe { ffi::io_surface_get_element_height(self.0) }
}
pub fn is_in_use(&self) -> bool {
unsafe { ffi::io_surface_is_in_use(self.0) }
}
pub fn increment_use_count(&self) {
unsafe { ffi::io_surface_increment_use_count(self.0) }
}
pub fn decrement_use_count(&self) {
unsafe { ffi::io_surface_decrement_use_count(self.0) }
}
pub(crate) fn base_address_raw(&self) -> *mut u8 {
unsafe { ffi::io_surface_get_base_address(self.0).cast::<u8>() }
}
pub(crate) fn base_address_of_plane_raw(&self, plane_index: usize) -> Option<*mut u8> {
let plane_count = self.plane_count();
if plane_count == 0 || plane_index >= plane_count {
return None;
}
let ptr = unsafe { ffi::io_surface_get_base_address_of_plane(self.0, plane_index) };
if ptr.is_null() {
None
} else {
Some(ptr.cast::<u8>())
}
}
pub fn lock_raw(&self, options: IOSurfaceLockOptions) -> Result<u32, i32> {
let mut seed: u32 = 0;
let status = unsafe { ffi::io_surface_lock(self.0, options.as_u32(), &mut seed) };
if status == 0 {
Ok(seed)
} else {
Err(status)
}
}
pub fn unlock_raw(&self, options: IOSurfaceLockOptions) -> Result<u32, i32> {
let mut seed: u32 = 0;
let status = unsafe { ffi::io_surface_unlock(self.0, options.as_u32(), &mut seed) };
if status == 0 {
Ok(seed)
} else {
Err(status)
}
}
pub fn lock(&self, options: IOSurfaceLockOptions) -> Result<IOSurfaceLockGuard<'_>, i32> {
self.lock_raw(options)?;
Ok(IOSurfaceLockGuard {
surface: self,
options,
})
}
pub fn lock_read_only(&self) -> Result<IOSurfaceLockGuard<'_>, i32> {
self.lock(IOSurfaceLockOptions::READ_ONLY)
}
pub fn lock_read_write(&self) -> Result<IOSurfaceLockGuard<'_>, i32> {
self.lock(IOSurfaceLockOptions::NONE)
}
}
pub struct IOSurfaceLockGuard<'a> {
surface: &'a IOSurface,
options: IOSurfaceLockOptions,
}
impl IOSurfaceLockGuard<'_> {
pub fn width(&self) -> usize {
self.surface.width()
}
pub fn height(&self) -> usize {
self.surface.height()
}
pub fn bytes_per_row(&self) -> usize {
self.surface.bytes_per_row()
}
pub fn alloc_size(&self) -> usize {
self.surface.alloc_size()
}
pub fn data_size(&self) -> usize {
self.alloc_size()
}
pub fn pixel_format(&self) -> u32 {
self.surface.pixel_format()
}
pub fn plane_count(&self) -> usize {
self.surface.plane_count()
}
pub fn base_address(&self) -> *const u8 {
self.surface.base_address_raw().cast_const()
}
pub fn base_address_mut(&mut self) -> Option<*mut u8> {
if self.options.is_read_only() {
None
} else {
Some(self.surface.base_address_raw())
}
}
pub fn base_address_of_plane(&self, plane_index: usize) -> Option<*const u8> {
self.surface
.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.options.is_read_only() {
return None;
}
self.surface.base_address_of_plane_raw(plane_index)
}
pub fn as_slice(&self) -> &[u8] {
let ptr = self.base_address();
let len = self.alloc_size();
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]> {
if self.options.is_read_only() {
return None;
}
let ptr = self.base_address_mut()?;
let len = self.alloc_size();
if ptr.is_null() || len == 0 {
Some(&mut [])
} else {
Some(unsafe { std::slice::from_raw_parts_mut(ptr, len) })
}
}
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 plane_data(&self, plane_index: usize) -> Option<&[u8]> {
let base = self.base_address_of_plane(plane_index)?;
let height = self.surface.height_of_plane(plane_index);
let bytes_per_row = self.surface.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]> {
let height = self.surface.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.surface.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 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.options.is_read_only()
}
pub const fn options(&self) -> IOSurfaceLockOptions {
self.options
}
}
impl std::ops::Deref for IOSurfaceLockGuard<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl Drop for IOSurfaceLockGuard<'_> {
fn drop(&mut self) {
let _ = self.surface.unlock_raw(self.options);
}
}
impl std::fmt::Debug for IOSurfaceLockGuard<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IOSurfaceLockGuard")
.field("options", &self.options)
.field(
"surface_size",
&(self.surface.width(), self.surface.height()),
)
.finish()
}
}
impl Drop for IOSurface {
fn drop(&mut self) {
unsafe {
ffi::io_surface_release(self.0);
}
}
}
impl Clone for IOSurface {
fn clone(&self) -> Self {
unsafe {
let ptr = ffi::io_surface_retain(self.0);
Self(ptr)
}
}
}
unsafe impl Send for IOSurface {}
unsafe impl Sync for IOSurface {}
impl fmt::Debug for IOSurface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IOSurface")
.field("id", &self.id())
.field("width", &self.width())
.field("height", &self.height())
.field("bytes_per_row", &self.bytes_per_row())
.field("pixel_format", &self.pixel_format())
.field("plane_count", &self.plane_count())
.finish()
}
}
impl fmt::Display for IOSurface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"IOSurface({}x{}, {} bytes/row)",
self.width(),
self.height(),
self.bytes_per_row()
)
}
}