#[cfg(feature = "v1_18")]
use std::num::NonZeroU16;
use std::os::raw::c_void;
use std::{ffi, ptr};
use four_cc::FourCC;
use libheif_sys as lh;
use crate::encoder::get_encoding_options_ptr;
use crate::reader::{Reader, HEIF_READER};
use crate::utils::str_to_cstring;
#[cfg(feature = "v1_19")]
use crate::SecurityLimits;
use crate::{
Encoder, EncodingOptions, HeifError, HeifErrorCode, HeifErrorSubCode, Image, ImageHandle,
ItemId, Result,
};
#[allow(dead_code)]
enum Source<'a> {
None,
File,
Memory(&'a [u8]),
Reader(Box<Box<dyn Reader>>),
}
pub struct HeifContext<'a> {
pub(crate) inner: *mut lh::heif_context,
source: Source<'a>,
}
impl HeifContext<'static> {
pub fn new() -> Result<HeifContext<'static>> {
let ctx = unsafe { lh::heif_context_alloc() };
if ctx.is_null() {
Err(HeifError {
code: HeifErrorCode::ContextCreateFailed,
sub_code: HeifErrorSubCode::Unspecified,
message: String::from(""),
})
} else {
Ok(HeifContext {
inner: ctx,
source: Source::None,
})
}
}
pub fn read_from_file(name: &str) -> Result<HeifContext<'static>> {
let mut context = HeifContext::new()?;
context.read_file(name)?;
Ok(context)
}
pub fn read_file(&mut self, name: &str) -> Result<()> {
self.source = Source::File;
let c_name = ffi::CString::new(name).unwrap();
let err =
unsafe { lh::heif_context_read_from_file(self.inner, c_name.as_ptr(), ptr::null()) };
HeifError::from_heif_error(err)?;
Ok(())
}
pub fn read_from_reader(reader: Box<dyn Reader>) -> Result<HeifContext<'static>> {
let mut context = HeifContext::new()?;
context.read_reader(reader)?;
Ok(context)
}
pub fn read_reader(&mut self, reader: Box<dyn Reader>) -> Result<()> {
let mut reader_box = Box::new(reader);
let user_data = reader_box.as_mut() as *mut _ as *mut c_void;
let err = unsafe {
lh::heif_context_read_from_reader(self.inner, &HEIF_READER, user_data, ptr::null())
};
HeifError::from_heif_error(err)?;
self.source = Source::Reader(reader_box);
Ok(())
}
#[cfg(feature = "v1_18")]
pub(crate) unsafe fn from_ptr(ctx: *mut lh::heif_context) -> HeifContext<'static> {
HeifContext {
inner: ctx,
source: Source::None,
}
}
}
impl<'a> HeifContext<'a> {
pub fn read_from_bytes(bytes: &[u8]) -> Result<HeifContext> {
let mut context = HeifContext::new()?;
context.read_bytes(bytes)?;
Ok(context)
}
pub fn read_bytes<'b: 'a>(&mut self, bytes: &'b [u8]) -> Result<()> {
self.source = Source::Memory(bytes);
let err = unsafe {
lh::heif_context_read_from_memory_without_copy(
self.inner,
bytes.as_ptr() as _,
bytes.len(),
ptr::null(),
)
};
HeifError::from_heif_error(err)?;
Ok(())
}
unsafe extern "C" fn vector_writer(
_ctx: *mut lh::heif_context,
data: *const c_void,
size: usize,
user_data: *mut c_void,
) -> lh::heif_error {
let vec: &mut Vec<u8> = &mut *(user_data as *mut Vec<u8>);
vec.reserve(size);
ptr::copy_nonoverlapping::<u8>(data as _, vec.as_mut_ptr(), size);
vec.set_len(size);
lh::heif_error {
code: lh::heif_error_code_heif_error_Ok,
subcode: lh::heif_suberror_code_heif_suberror_Unspecified,
message: b"\0".as_ptr() as _,
}
}
pub fn write_to_bytes(&self) -> Result<Vec<u8>> {
let mut res = Vec::<u8>::new();
let pointer_to_res = &mut res as *mut _ as *mut c_void;
let mut writer = lh::heif_writer {
writer_api_version: 1,
write: Some(Self::vector_writer),
};
let err = unsafe { lh::heif_context_write(self.inner, &mut writer, pointer_to_res) };
HeifError::from_heif_error(err)?;
Ok(res)
}
pub fn write_to_file(&self, name: &str) -> Result<()> {
let c_name = ffi::CString::new(name).unwrap();
let err = unsafe { lh::heif_context_write_to_file(self.inner, c_name.as_ptr()) };
HeifError::from_heif_error(err)
}
pub fn number_of_top_level_images(&self) -> usize {
unsafe { lh::heif_context_get_number_of_top_level_images(self.inner) as _ }
}
pub fn top_level_image_ids(&self, item_ids: &mut [ItemId]) -> usize {
if item_ids.is_empty() {
0
} else {
unsafe {
lh::heif_context_get_list_of_top_level_image_IDs(
self.inner,
item_ids.as_mut_ptr(),
item_ids.len() as _,
) as usize
}
}
}
pub fn image_handle(&self, item_id: ItemId) -> Result<ImageHandle> {
let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
let err = unsafe { lh::heif_context_get_image_handle(self.inner, item_id, &mut handle) };
HeifError::from_heif_error(err)?;
Ok(ImageHandle::new(handle))
}
pub fn primary_image_handle(&self) -> Result<ImageHandle> {
let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
let err = unsafe { lh::heif_context_get_primary_image_handle(self.inner, &mut handle) };
HeifError::from_heif_error(err)?;
Ok(ImageHandle::new(handle))
}
pub fn top_level_image_handles(&self) -> Vec<ImageHandle> {
let max_count = self.number_of_top_level_images();
let mut item_ids = Vec::with_capacity(max_count);
unsafe {
let count = lh::heif_context_get_list_of_top_level_image_IDs(
self.inner,
item_ids.as_mut_ptr(),
max_count as _,
) as usize;
item_ids.set_len(count);
}
let mut handles = Vec::with_capacity(item_ids.len());
for item_id in item_ids {
if let Ok(handle) = self.image_handle(item_id) {
handles.push(handle);
}
}
handles
}
pub fn encode_image(
&mut self,
image: &Image,
encoder: &mut Encoder,
encoding_options: Option<EncodingOptions>,
) -> Result<ImageHandle> {
let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
unsafe {
let err = lh::heif_context_encode_image(
self.inner,
image.inner,
encoder.inner,
get_encoding_options_ptr(&encoding_options),
&mut handle,
);
HeifError::from_heif_error(err)?;
}
Ok(ImageHandle::new(handle))
}
pub fn encode_thumbnail(
&mut self,
image: &Image,
master_image_handle: &ImageHandle,
bbox_size: u32,
encoder: &mut Encoder,
encoding_options: Option<EncodingOptions>,
) -> Result<Option<ImageHandle>> {
let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
unsafe {
let err = lh::heif_context_encode_thumbnail(
self.inner,
image.inner,
master_image_handle.inner,
encoder.inner,
get_encoding_options_ptr(&encoding_options),
bbox_size.min(i32::MAX as _) as _,
&mut handle,
);
HeifError::from_heif_error(err)?;
}
Ok(Some(ImageHandle::new(handle)))
}
#[cfg(feature = "v1_18")]
pub fn encode_grid(
&mut self,
tiles: &[Image],
rows: NonZeroU16,
encoder: &mut Encoder,
encoding_options: Option<EncodingOptions>,
) -> Result<Option<ImageHandle>> {
let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
let mut tiles_inners: Vec<*mut lh::heif_image> =
tiles.iter().map(|img| img.inner).collect();
let rows = rows.get();
let columns = (tiles_inners.len() as u32 / rows as u32).min(u16::MAX as _) as u16;
unsafe {
let err = lh::heif_context_encode_grid(
self.inner,
tiles_inners.as_mut_ptr(),
rows,
columns,
encoder.inner,
get_encoding_options_ptr(&encoding_options),
&mut handle,
);
HeifError::from_heif_error(err)?;
}
Ok(Some(ImageHandle::new(handle)))
}
pub fn assign_thumbnail(
&mut self,
master_image_handle: &ImageHandle,
thumbnail_image_handle: &ImageHandle,
) -> Result<()> {
unsafe {
let err = lh::heif_context_assign_thumbnail(
self.inner,
master_image_handle.inner,
thumbnail_image_handle.inner,
);
HeifError::from_heif_error(err)
}
}
pub fn set_primary_image(&mut self, image_handle: &mut ImageHandle) -> Result<()> {
unsafe {
let err = lh::heif_context_set_primary_image(self.inner, image_handle.inner);
HeifError::from_heif_error(err)
}
}
pub fn add_generic_metadata<T>(
&mut self,
image_handle: &ImageHandle,
data: &[u8],
item_type: T,
content_type: Option<&str>,
) -> Result<()>
where
T: Into<FourCC>,
{
let c_item_type = str_to_cstring(&item_type.into().to_string(), "item_type")?;
let c_content_type = match content_type {
Some(s) => Some(str_to_cstring(s, "content_type")?),
None => None,
};
let c_content_type_ptr = c_content_type.map(|s| s.as_ptr()).unwrap_or(ptr::null());
let error = unsafe {
lh::heif_context_add_generic_metadata(
self.inner,
image_handle.inner,
data.as_ptr() as _,
data.len() as _,
c_item_type.as_ptr(),
c_content_type_ptr,
)
};
HeifError::from_heif_error(error)
}
pub fn add_exif_metadata(&mut self, master_image: &ImageHandle, data: &[u8]) -> Result<()> {
let error = unsafe {
lh::heif_context_add_exif_metadata(
self.inner,
master_image.inner,
data.as_ptr() as _,
data.len() as _,
)
};
HeifError::from_heif_error(error)
}
pub fn add_xmp_metadata(&mut self, master_image: &ImageHandle, data: &[u8]) -> Result<()> {
let error = unsafe {
lh::heif_context_add_XMP_metadata(
self.inner,
master_image.inner,
data.as_ptr() as _,
data.len() as _,
)
};
HeifError::from_heif_error(error)
}
pub fn set_max_decoding_threads(&mut self, max_threads: u32) {
let max_threads = max_threads.min(libc::c_int::MAX as u32) as libc::c_int;
unsafe { lh::heif_context_set_max_decoding_threads(self.inner, max_threads) };
}
#[cfg(feature = "v1_19")]
pub fn security_limits(&self) -> SecurityLimits {
let inner_ptr = unsafe { lh::heif_context_get_security_limits(self.inner) };
let inner = ptr::NonNull::new(inner_ptr).unwrap();
SecurityLimits::from_inner(inner)
}
#[cfg(feature = "v1_19")]
pub fn set_security_limits(&mut self, limits: &SecurityLimits) -> Result<()> {
let err = unsafe { lh::heif_context_set_security_limits(self.inner, limits.as_inner()) };
HeifError::from_heif_error(err)
}
}
impl Drop for HeifContext<'_> {
fn drop(&mut self) {
unsafe { lh::heif_context_free(self.inner) };
}
}
unsafe impl Send for HeifContext<'_> {}