use std::{mem, ops::Deref, os::unix::ffi::OsStrExt, path::Path, pin::Pin};
use cxx::UniquePtr;
use crate::{
error::BrawError,
ffi,
format::{Pipeline, ResolutionScale, ResourceFormat},
handler::{Callback, CallbackBridge},
};
pub struct Codec(UniquePtr<ffi::BrawCodec>);
impl Codec {
pub fn new<C: Callback>(callback: C) -> Result<Self, BrawError> {
let c = Box::new(CallbackBridge::new(callback));
Ok(Self(ffi::new_codec(c)?))
}
pub fn prepare_pipeline(&mut self, pipeline: Pipeline) -> Result<(), BrawError> {
self.pin_mut().prepare_pipeline(pipeline as u32)?;
Ok(())
}
pub fn open_clip(&mut self, path: impl AsRef<Path>) -> Result<Clip, BrawError> {
self.do_open_clip(path.as_ref())
}
fn do_open_clip(&mut self, path: &Path) -> Result<Clip, BrawError> {
let bytes = path.as_os_str().as_bytes();
if bytes.contains(&0) {
return Err(BrawError::new("path contains an interior NUL byte"));
}
Ok(Clip(self.pin_mut().open_clip(bytes)?))
}
pub fn flush_jobs(&mut self) {
self.pin_mut().flush_jobs();
}
fn pin_mut(&mut self) -> Pin<&mut ffi::BrawCodec> {
self.0.pin_mut()
}
}
pub struct Clip(UniquePtr<ffi::BrawClip>);
impl Clip {
pub fn width(&self) -> u32 {
self.0.width()
}
pub fn height(&self) -> u32 {
self.0.height()
}
pub fn frame_count(&self) -> u64 {
self.0.frame_count()
}
pub fn frame_rate(&self) -> f32 {
self.0.frame_rate()
}
pub fn create_read_job(&mut self, frame_index: u64) -> Result<Job, BrawError> {
Ok(Job(self.0.pin_mut().create_job_read_frame(frame_index)?))
}
}
pub struct Frame(UniquePtr<ffi::BrawFrame>);
impl Frame {
pub(crate) fn from_cxx(inner: UniquePtr<ffi::BrawFrame>) -> Self {
Self(inner)
}
pub fn set_resource_format(&mut self, format: ResourceFormat) -> Result<(), BrawError> {
self.0.pin_mut().set_resource_format(format as u32)?;
Ok(())
}
pub fn set_resolution_scale(&mut self, scale: ResolutionScale) -> Result<(), BrawError> {
self.0.pin_mut().set_resolution_scale(scale as u32)?;
Ok(())
}
pub fn create_decode_and_process_job(&mut self) -> Result<Job, BrawError> {
Ok(Job(self.0.pin_mut().create_job_decode_and_process()?))
}
}
pub struct ProcessedImage {
inner: UniquePtr<ffi::BrawProcessedImage>,
width: u32,
height: u32,
format: ResourceFormat,
size_bytes: u32,
pixel_len: usize,
}
impl ProcessedImage {
pub(crate) fn from_cxx(inner: UniquePtr<ffi::BrawProcessedImage>) -> Self {
let width = inner.image_width();
let height = inner.image_height();
let raw_format = inner.image_format();
let format = ResourceFormat::from_raw(raw_format).unwrap_or_else(|| {
panic!("warb: SDK returned unknown ResourceFormat 0x{raw_format:08x}")
});
let size_bytes = inner.image_size_bytes();
let pixel_len = (width as usize)
.saturating_mul(height as usize)
.saturating_mul(format.bytes_per_pixel() as usize);
Self {
inner,
width,
height,
format,
size_bytes,
pixel_len,
}
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn format(&self) -> ResourceFormat {
self.format
}
pub fn size_bytes(&self) -> u32 {
self.size_bytes
}
pub fn data(&self) -> &[u8] {
let full = self.inner.image_data();
&full[..self.pixel_len.min(full.len())]
}
pub fn full_data(&self) -> &[u8] {
self.inner.image_data()
}
}
impl Deref for ProcessedImage {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.data()
}
}
impl AsRef<[u8]> for ProcessedImage {
fn as_ref(&self) -> &[u8] {
self.data()
}
}
pub struct Job(UniquePtr<ffi::BrawJob>);
impl Job {
pub fn set_user_data(&mut self, user_data: u64) -> Result<(), BrawError> {
self.0.pin_mut().set_user_data(user_data)?;
Ok(())
}
pub fn abort(&mut self) -> Result<(), BrawError> {
self.0.pin_mut().abort_job()?;
Ok(())
}
pub fn submit(mut self) -> Result<(), BrawError> {
let inner = mem::replace(&mut self.0, UniquePtr::null());
ffi::submit_job(inner)?;
Ok(())
}
}
unsafe impl Send for Codec {}
unsafe impl Send for Clip {}
unsafe impl Send for Frame {}
unsafe impl Send for ProcessedImage {}
unsafe impl Send for Job {}