use core_foundation::base::TCFType;
use core_foundation::boolean::CFBoolean;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::CFNumber;
use core_foundation::string::CFString;
use core_foundation_sys::base::kCFAllocatorDefault;
use core_foundation_sys::dictionary::CFDictionaryRef;
use std::ptr;
use super::cv_ffi::{
kCVPixelBufferCGBitmapContextCompatibilityKey, kCVPixelBufferCGImageCompatibilityKey,
kCVPixelBufferHeightKey, kCVPixelBufferPixelFormatTypeKey, kCVPixelBufferWidthKey,
kCVReturnSuccess, CVPixelBufferCreate, CVPixelBufferGetBaseAddress,
CVPixelBufferGetBytesPerRow, CVPixelBufferLockBaseAddress, CVPixelBufferUnlockBaseAddress,
};
use crate::codecs;
use crate::cv_types::CVPixelBufferRef;
#[derive(Clone)]
pub struct PixelBufferConfig {
pub width: usize,
pub height: usize,
pub pixel_format: u32,
pub cg_compatible: bool,
pub cg_bitmap_compatible: bool,
}
impl PixelBufferConfig {
pub fn new(width: usize, height: usize) -> Self {
Self {
width,
height,
pixel_format: codecs::pixel::BGRA32,
cg_compatible: true,
cg_bitmap_compatible: true,
}
}
pub fn pixel_format(mut self, format: u32) -> Self {
self.pixel_format = format;
self
}
pub fn cg_compatible(mut self, enabled: bool) -> Self {
self.cg_compatible = enabled;
self
}
pub fn cg_bitmap_compatible(mut self, enabled: bool) -> Self {
self.cg_bitmap_compatible = enabled;
self
}
}
pub fn create_pixel_buffer(config: &PixelBufferConfig) -> Result<CVPixelBufferRef, i32> {
unsafe {
let mut pixel_buffer: CVPixelBufferRef = ptr::null_mut();
let format_key = CFString::wrap_under_get_rule(kCVPixelBufferPixelFormatTypeKey);
let width_key = CFString::wrap_under_get_rule(kCVPixelBufferWidthKey);
let height_key = CFString::wrap_under_get_rule(kCVPixelBufferHeightKey);
let mut pairs = vec![
(
format_key.as_CFType(),
CFNumber::from(config.pixel_format as i32).as_CFType(),
),
(
width_key.as_CFType(),
CFNumber::from(config.width as i32).as_CFType(),
),
(
height_key.as_CFType(),
CFNumber::from(config.height as i32).as_CFType(),
),
];
if config.cg_compatible {
let cg_key = CFString::wrap_under_get_rule(kCVPixelBufferCGImageCompatibilityKey);
pairs.push((cg_key.as_CFType(), CFBoolean::true_value().as_CFType()));
}
if config.cg_bitmap_compatible {
let cg_bitmap_key =
CFString::wrap_under_get_rule(kCVPixelBufferCGBitmapContextCompatibilityKey);
pairs.push((
cg_bitmap_key.as_CFType(),
CFBoolean::true_value().as_CFType(),
));
}
let attrs = CFDictionary::from_CFType_pairs(&pairs);
let status = CVPixelBufferCreate(
kCFAllocatorDefault,
config.width,
config.height,
config.pixel_format,
attrs.as_concrete_TypeRef() as CFDictionaryRef,
&mut pixel_buffer,
);
if status != kCVReturnSuccess {
return Err(status);
}
Ok(pixel_buffer)
}
}
pub struct PixelBufferGuard {
pixel_buffer: CVPixelBufferRef,
base_address: *mut u8,
bytes_per_row: usize,
}
impl PixelBufferGuard {
pub unsafe fn lock(pixel_buffer: CVPixelBufferRef) -> Result<Self, i32> {
let status = CVPixelBufferLockBaseAddress(pixel_buffer, 0);
if status != kCVReturnSuccess {
return Err(status);
}
let base_address = CVPixelBufferGetBaseAddress(pixel_buffer) as *mut u8;
let bytes_per_row = CVPixelBufferGetBytesPerRow(pixel_buffer);
Ok(Self {
pixel_buffer,
base_address,
bytes_per_row,
})
}
pub fn base_address(&self) -> *mut u8 {
self.base_address
}
pub fn bytes_per_row(&self) -> usize {
self.bytes_per_row
}
pub fn pixel_buffer(&self) -> CVPixelBufferRef {
self.pixel_buffer
}
}
impl Drop for PixelBufferGuard {
fn drop(&mut self) {
unsafe {
CVPixelBufferUnlockBaseAddress(self.pixel_buffer, 0);
}
}
}