use crate::error::{Error, Result};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use whereat::*;
pub(crate) struct Demux<'a> {
inner: NonNull<libwebp_sys::WebPDemuxer>,
_data: PhantomData<&'a [u8]>,
}
impl<'a> Demux<'a> {
pub(crate) fn new(data: &'a [u8]) -> Result<Self> {
let webp_data = libwebp_sys::WebPData {
bytes: data.as_ptr(),
size: data.len(),
};
let demuxer = unsafe {
libwebp_sys::WebPDemuxInternal(
&webp_data,
0,
core::ptr::null_mut(),
libwebp_sys::WEBP_DEMUX_ABI_VERSION as i32,
)
};
match NonNull::new(demuxer) {
Some(nn) => Ok(Self {
inner: nn,
_data: PhantomData,
}),
None => Err(at!(Error::InvalidWebP)),
}
}
#[allow(dead_code)] pub(crate) fn as_ptr(&self) -> *mut libwebp_sys::WebPDemuxer {
self.inner.as_ptr()
}
pub(crate) fn get_chunk<'demux>(&'demux self, fourcc: &[u8; 4]) -> Option<ChunkIter<'demux>> {
let mut iter = MaybeUninit::<libwebp_sys::WebPChunkIterator>::zeroed();
let found = unsafe {
libwebp_sys::WebPDemuxGetChunk(
self.inner.as_ptr(),
fourcc.as_ptr() as *const core::ffi::c_char,
1,
iter.as_mut_ptr(),
)
};
if found == 0 {
None
} else {
Some(ChunkIter {
inner: unsafe { iter.assume_init() },
_demux: PhantomData,
})
}
}
}
impl Drop for Demux<'_> {
fn drop(&mut self) {
unsafe { libwebp_sys::WebPDemuxDelete(self.inner.as_ptr()) };
}
}
pub(crate) struct ChunkIter<'demux> {
inner: libwebp_sys::WebPChunkIterator,
_demux: PhantomData<&'demux Demux<'demux>>,
}
impl ChunkIter<'_> {
pub(crate) fn bytes(&self) -> &[u8] {
if self.inner.chunk.bytes.is_null() || self.inner.chunk.size == 0 {
&[]
} else {
unsafe { core::slice::from_raw_parts(self.inner.chunk.bytes, self.inner.chunk.size) }
}
}
}
impl Drop for ChunkIter<'_> {
fn drop(&mut self) {
unsafe { libwebp_sys::WebPDemuxReleaseChunkIterator(&mut self.inner) };
}
}