use std::ffi::CString;
use std::path::Path;
use std::ptr;
use libheif_sys as lh;
use crate::decoder::get_decoding_options_ptr;
use crate::utils::path_to_cstring;
use crate::{
ColorSpace, CompressionFormat, DecoderDescriptor, DecodingOptions, Encoder, EncoderDescriptor,
HeifError, Image, ImageHandle, Result,
};
pub struct LibHeif(());
impl LibHeif {
pub fn new() -> Self {
let mut init_params = lh::heif_init_params { version: 0 };
unsafe {
lh::heif_init(&mut init_params as _);
}
Self(())
}
pub fn new_checked() -> Result<Self> {
let mut init_params = lh::heif_init_params { version: 0 };
let error = unsafe { lh::heif_init(&mut init_params as _) };
HeifError::from_heif_error(error)?;
Ok(Self(()))
}
}
impl Drop for LibHeif {
fn drop(&mut self) {
unsafe {
lh::heif_deinit();
}
}
}
impl Default for LibHeif {
fn default() -> Self {
Self::new()
}
}
impl LibHeif {
pub fn version(&self) -> [u8; 3] {
let version: u32 = unsafe { lh::heif_get_version_number() };
let parts = version.to_be_bytes();
[parts[0], parts[1], parts[2]]
}
pub fn load_plugins(&self, dir_path: impl AsRef<Path>) -> Result<usize> {
self._load_plugins(dir_path.as_ref())
}
fn _load_plugins(&self, dir_path: &Path) -> Result<usize> {
let dir_path = path_to_cstring(dir_path);
let mut plugins_loaded: libc::c_int = 0;
let err = unsafe {
lh::heif_load_plugins(
dir_path.as_ptr(),
ptr::null_mut() as _,
&mut plugins_loaded as _,
0,
)
};
HeifError::from_heif_error(err)?;
Ok(plugins_loaded as usize)
}
pub fn decode(
&self,
image_handle: &ImageHandle,
color_space: ColorSpace,
decoding_options: Option<DecodingOptions>,
) -> Result<Image> {
let mut c_image: *mut lh::heif_image = ptr::null_mut();
let err = unsafe {
lh::heif_decode_image(
image_handle.inner,
&mut c_image,
color_space.heif_color_space(),
color_space.heif_chroma(),
get_decoding_options_ptr(&decoding_options),
)
};
HeifError::from_heif_error(err)?;
Ok(Image::from_heif_image(c_image))
}
pub fn decoder_descriptors(
&self,
max_count: usize,
format_filter: Option<CompressionFormat>,
) -> Vec<DecoderDescriptor> {
let format_filter = format_filter.unwrap_or(CompressionFormat::Undefined);
let max_count = max_count.min(libc::c_int::MAX as usize);
let mut descriptors_ptr = Vec::with_capacity(max_count);
unsafe {
let count = lh::heif_get_decoder_descriptors(
format_filter as _,
descriptors_ptr.as_mut_ptr(),
max_count as _,
);
descriptors_ptr.set_len(count as usize);
}
descriptors_ptr
.into_iter()
.filter_map(|d_ptr| unsafe { d_ptr.as_ref().map(DecoderDescriptor::new) })
.collect()
}
pub fn encoder_descriptors(
&self,
max_count: usize,
format_filter: Option<CompressionFormat>,
name_filter: Option<&str>,
) -> Vec<EncoderDescriptor> {
let format_filter = format_filter.unwrap_or(CompressionFormat::Undefined);
let max_count = max_count.min(libc::c_int::MAX as usize);
let name_filter = name_filter
.map(|s| CString::new(s).ok())
.unwrap_or_default();
let name_filter_ptr = name_filter.map(|s| s.as_ptr()).unwrap_or(ptr::null());
let mut descriptors_ptr = Vec::with_capacity(max_count);
unsafe {
let count = lh::heif_get_encoder_descriptors(
format_filter as _,
name_filter_ptr,
descriptors_ptr.as_mut_ptr(),
max_count as _,
);
descriptors_ptr.set_len(count as usize);
}
descriptors_ptr
.into_iter()
.filter_map(|d_ptr| unsafe { d_ptr.as_ref().map(EncoderDescriptor::new) })
.collect()
}
pub fn encoder(&self, descriptor: EncoderDescriptor) -> Result<Encoder> {
let mut c_encoder: *mut lh::heif_encoder = ptr::null_mut();
let err = unsafe {
lh::heif_context_get_encoder(ptr::null_mut(), descriptor.inner, &mut c_encoder)
};
HeifError::from_heif_error(err)?;
let encoder = Encoder::new(unsafe { &mut *c_encoder })?;
Ok(encoder)
}
pub fn encoder_for_format(&self, format: CompressionFormat) -> Result<Encoder> {
let mut c_encoder: *mut lh::heif_encoder = ptr::null_mut();
let err = unsafe {
lh::heif_context_get_encoder_for_format(
ptr::null_mut() as _,
format as _,
&mut c_encoder,
)
};
HeifError::from_heif_error(err)?;
let encoder = Encoder::new(unsafe { &mut *c_encoder })?;
Ok(encoder)
}
}