use crate::error::{Error, MuxError, Result};
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use whereat::*;
pub fn get_icc_profile(webp_data: &[u8]) -> Result<Option<Vec<u8>>> {
get_chunk(webp_data, b"ICCP")
}
pub fn get_exif(webp_data: &[u8]) -> Result<Option<Vec<u8>>> {
get_chunk(webp_data, b"EXIF")
}
pub fn get_xmp(webp_data: &[u8]) -> Result<Option<Vec<u8>>> {
get_chunk(webp_data, b"XMP ")
}
unsafe fn create_demux(webp_data: &[u8]) -> *mut libwebp_sys::WebPDemuxer {
let data = libwebp_sys::WebPData {
bytes: webp_data.as_ptr(),
size: webp_data.len(),
};
unsafe {
libwebp_sys::WebPDemuxInternal(
&data,
0, core::ptr::null_mut(),
libwebp_sys::WEBP_DEMUX_ABI_VERSION as i32,
)
}
}
unsafe fn create_mux_from_data(webp_data: &[u8], copy_data: bool) -> *mut libwebp_sys::WebPMux {
let data = libwebp_sys::WebPData {
bytes: webp_data.as_ptr(),
size: webp_data.len(),
};
unsafe {
libwebp_sys::WebPMuxCreateInternal(
&data,
copy_data as i32,
libwebp_sys::WEBP_MUX_ABI_VERSION as i32,
)
}
}
fn get_chunk(webp_data: &[u8], fourcc: &[u8; 4]) -> Result<Option<Vec<u8>>> {
let demux = unsafe { create_demux(webp_data) };
if demux.is_null() {
return Err(at!(Error::InvalidWebP));
}
let mut chunk_iter = MaybeUninit::<libwebp_sys::WebPChunkIterator>::zeroed();
let found = unsafe {
libwebp_sys::WebPDemuxGetChunk(
demux,
fourcc.as_ptr() as *const core::ffi::c_char,
1, chunk_iter.as_mut_ptr(),
)
};
let result = if found != 0 {
let chunk_iter = unsafe { chunk_iter.assume_init() };
if !chunk_iter.chunk.bytes.is_null() && chunk_iter.chunk.size > 0 {
let chunk_data = unsafe {
core::slice::from_raw_parts(chunk_iter.chunk.bytes, chunk_iter.chunk.size)
};
let vec = Some(chunk_data.to_vec());
unsafe {
let mut iter = chunk_iter;
libwebp_sys::WebPDemuxReleaseChunkIterator(&mut iter);
}
vec
} else {
None
}
} else {
None
};
unsafe {
libwebp_sys::WebPDemuxDelete(demux);
}
Ok(result)
}
pub fn embed_icc(webp_data: &[u8], icc_profile: &[u8]) -> Result<Vec<u8>> {
embed_chunk(webp_data, b"ICCP", icc_profile)
}
pub fn embed_exif(webp_data: &[u8], exif_data: &[u8]) -> Result<Vec<u8>> {
embed_chunk(webp_data, b"EXIF", exif_data)
}
pub fn embed_xmp(webp_data: &[u8], xmp_data: &[u8]) -> Result<Vec<u8>> {
embed_chunk(webp_data, b"XMP ", xmp_data)
}
fn embed_chunk(webp_data: &[u8], fourcc: &[u8; 4], chunk_data: &[u8]) -> Result<Vec<u8>> {
let mux = unsafe { create_mux_from_data(webp_data, true) };
if mux.is_null() {
return Err(at!(Error::MuxError(MuxError::BadData)));
}
let chunk = libwebp_sys::WebPData {
bytes: chunk_data.as_ptr(),
size: chunk_data.len(),
};
let err = unsafe {
libwebp_sys::WebPMuxSetChunk(
mux,
fourcc.as_ptr() as *const core::ffi::c_char,
&chunk,
1, )
};
if err != libwebp_sys::WebPMuxError::WEBP_MUX_OK {
unsafe { libwebp_sys::WebPMuxDelete(mux) };
return Err(at!(Error::MuxError(MuxError::from(err as i32))));
}
let mut output_data = libwebp_sys::WebPData::default();
let err = unsafe { libwebp_sys::WebPMuxAssemble(mux, &mut output_data) };
if err != libwebp_sys::WebPMuxError::WEBP_MUX_OK {
unsafe { libwebp_sys::WebPMuxDelete(mux) };
return Err(at!(Error::MuxError(MuxError::from(err as i32))));
}
let result = unsafe {
if output_data.bytes.is_null() || output_data.size == 0 {
libwebp_sys::WebPMuxDelete(mux);
return Err(at!(Error::MuxError(MuxError::MemoryError)));
}
let slice = core::slice::from_raw_parts(output_data.bytes, output_data.size);
let vec = slice.to_vec();
libwebp_sys::WebPDataClear(&mut output_data);
libwebp_sys::WebPMuxDelete(mux);
vec
};
Ok(result)
}
pub fn remove_icc(webp_data: &[u8]) -> Result<Vec<u8>> {
remove_chunk(webp_data, b"ICCP")
}
pub fn remove_exif(webp_data: &[u8]) -> Result<Vec<u8>> {
remove_chunk(webp_data, b"EXIF")
}
pub fn remove_xmp(webp_data: &[u8]) -> Result<Vec<u8>> {
remove_chunk(webp_data, b"XMP ")
}
fn remove_chunk(webp_data: &[u8], fourcc: &[u8; 4]) -> Result<Vec<u8>> {
let mux = unsafe { create_mux_from_data(webp_data, true) };
if mux.is_null() {
return Err(at!(Error::MuxError(MuxError::BadData)));
}
let err = unsafe {
libwebp_sys::WebPMuxDeleteChunk(mux, fourcc.as_ptr() as *const core::ffi::c_char)
};
if err != libwebp_sys::WebPMuxError::WEBP_MUX_OK
&& err != libwebp_sys::WebPMuxError::WEBP_MUX_NOT_FOUND
{
unsafe { libwebp_sys::WebPMuxDelete(mux) };
return Err(at!(Error::MuxError(MuxError::from(err as i32))));
}
let mut output_data = libwebp_sys::WebPData::default();
let err = unsafe { libwebp_sys::WebPMuxAssemble(mux, &mut output_data) };
if err != libwebp_sys::WebPMuxError::WEBP_MUX_OK {
unsafe { libwebp_sys::WebPMuxDelete(mux) };
return Err(at!(Error::MuxError(MuxError::from(err as i32))));
}
let result = unsafe {
if output_data.bytes.is_null() || output_data.size == 0 {
libwebp_sys::WebPMuxDelete(mux);
return Err(at!(Error::MuxError(MuxError::MemoryError)));
}
let slice = core::slice::from_raw_parts(output_data.bytes, output_data.size);
let vec = slice.to_vec();
libwebp_sys::WebPDataClear(&mut output_data);
libwebp_sys::WebPMuxDelete(mux);
vec
};
Ok(result)
}
#[cfg(test)]
mod tests {
}