use crate::utils::{cstr_to_str, str_to_cstring};
use crate::{ChromaDownsamplingAlgorithm, ChromaUpsamplingAlgorithm, HeifError};
use libheif_sys as lh;
use std::ffi::CString;
use std::fmt::{Debug, Formatter};
use std::ptr;
use std::sync::Mutex;
static DECODER_MUTEX: Mutex<()> = Mutex::new(());
#[derive(Debug)]
pub struct DecodingOptions {
inner: ptr::NonNull<lh::heif_decoding_options>,
decoder_id: Option<CString>,
}
impl DecodingOptions {
pub fn new() -> Option<Self> {
let inner_ptr = unsafe { lh::heif_decoding_options_alloc() };
ptr::NonNull::new(inner_ptr).map(|inner| Self {
inner,
decoder_id: None,
})
}
}
impl Drop for DecodingOptions {
fn drop(&mut self) {
unsafe {
lh::heif_decoding_options_free(self.inner.as_ptr());
}
}
}
impl DecodingOptions {
#[inline(always)]
fn inner_ref(&self) -> &lh::heif_decoding_options {
unsafe { self.inner.as_ref() }
}
#[inline(always)]
pub(crate) fn inner_mut(&mut self) -> &mut lh::heif_decoding_options {
unsafe { self.inner.as_mut() }
}
#[inline]
pub fn version(&self) -> u8 {
self.inner_ref().version
}
#[inline]
pub fn ignore_transformations(&self) -> bool {
self.inner_ref().ignore_transformations != 0
}
#[inline]
pub fn set_ignore_transformations(&mut self, enable: bool) {
self.inner_mut().ignore_transformations = if enable { 1 } else { 0 }
}
#[inline]
pub fn convert_hdr_to_8bit(&self) -> bool {
self.inner_ref().convert_hdr_to_8bit != 0
}
#[inline]
pub fn set_convert_hdr_to_8bit(&mut self, enable: bool) {
self.inner_mut().convert_hdr_to_8bit = if enable { 1 } else { 0 }
}
pub fn strict_decoding(&self) -> bool {
self.inner_ref().strict_decoding != 0
}
pub fn set_strict_decoding(&mut self, enable: bool) {
self.inner_mut().strict_decoding = if enable { 1 } else { 0 }
}
pub fn decoder_id(&self) -> Option<&str> {
cstr_to_str(self.inner_ref().decoder_id)
}
pub fn set_decoder_id(&mut self, decoder_id: Option<&str>) -> Result<(), HeifError> {
if let Some(decoder_id) = decoder_id {
let c_decoder_id = str_to_cstring(decoder_id, "decoder_id")?;
self.inner_mut().decoder_id = c_decoder_id.as_ptr();
self.decoder_id = Some(c_decoder_id);
} else {
self.inner_mut().decoder_id = ptr::null() as _;
self.decoder_id = None;
}
Ok(())
}
pub fn color_conversion_options(&self) -> ColorConversionOptions {
let lh_options = self.inner_ref().color_conversion_options;
ColorConversionOptions {
preferred_chroma_downsampling_algorithm: ChromaDownsamplingAlgorithm::n(
lh_options.preferred_chroma_downsampling_algorithm,
)
.unwrap_or(ChromaDownsamplingAlgorithm::Average),
preferred_chroma_upsampling_algorithm: ChromaUpsamplingAlgorithm::n(
lh_options.preferred_chroma_upsampling_algorithm,
)
.unwrap_or(ChromaUpsamplingAlgorithm::Bilinear),
only_use_preferred_chroma_algorithm: lh_options.only_use_preferred_chroma_algorithm
!= 0,
}
}
pub fn set_color_conversion_options(&mut self, options: ColorConversionOptions) {
let lh_options = &mut self.inner_mut().color_conversion_options;
lh_options.preferred_chroma_downsampling_algorithm =
options.preferred_chroma_downsampling_algorithm as _;
lh_options.preferred_chroma_upsampling_algorithm =
options.preferred_chroma_upsampling_algorithm as _;
lh_options.only_use_preferred_chroma_algorithm =
options.only_use_preferred_chroma_algorithm as _;
}
}
pub(crate) fn get_decoding_options_ptr(
options: &Option<DecodingOptions>,
) -> *mut lh::heif_decoding_options {
options
.as_ref()
.map(|o| o.inner.as_ptr())
.unwrap_or_else(ptr::null_mut)
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ColorConversionOptions {
pub preferred_chroma_downsampling_algorithm: ChromaDownsamplingAlgorithm,
pub preferred_chroma_upsampling_algorithm: ChromaUpsamplingAlgorithm,
pub only_use_preferred_chroma_algorithm: bool,
}
#[derive(Copy, Clone)]
pub struct DecoderDescriptor<'a> {
inner: &'a lh::heif_decoder_descriptor,
}
impl<'a> DecoderDescriptor<'a> {
pub(crate) fn new(inner: &'a lh::heif_decoder_descriptor) -> Self {
Self { inner }
}
pub fn id(&self) -> &str {
let name = unsafe { lh::heif_decoder_descriptor_get_id_name(self.inner) };
cstr_to_str(name).unwrap_or_default()
}
pub fn name(&self) -> String {
let _lock = DECODER_MUTEX.lock();
let name = unsafe { lh::heif_decoder_descriptor_get_name(self.inner) };
cstr_to_str(name).unwrap_or_default().to_owned()
}
}
impl<'a> Debug for DecoderDescriptor<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecoderDescriptor")
.field("id", &self.id())
.field("name", &self.name())
.finish()
}
}