use std::ffi::CString;
use std::fmt::{Debug, Formatter};
use std::ptr;
use std::sync::Mutex;
use libheif_sys as lh;
use crate::utils::{cstr_to_str, str_to_cstring};
#[cfg(feature = "v1_20")]
use crate::AlphaCompositionMode;
use crate::{ChromaDownsamplingAlgorithm, ChromaUpsamplingAlgorithm, ColorProfileNCLX, HeifError};
static DECODER_MUTEX: Mutex<()> = Mutex::new(());
#[derive(Debug)]
pub struct DecodingOptions {
inner: ptr::NonNull<lh::heif_decoding_options>,
decoder_id: Option<CString>,
#[allow(dead_code)]
output_image_nclx_profile: Option<ColorProfileNCLX>,
}
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,
output_image_nclx_profile: None,
})
}
}
impl Drop for DecodingOptions {
fn drop(&mut self) {
#[cfg(feature = "v1_20")]
{
let inner_mut = self.inner_mut();
if !inner_mut.color_conversion_options_ext.is_null() {
unsafe {
lh::heif_color_conversion_options_ext_free(
inner_mut.color_conversion_options_ext,
)
};
inner_mut.color_conversion_options_ext = ptr::null_mut();
}
}
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 inner = self.inner_ref();
ColorConversionOptions::from_cc_options(&inner.color_conversion_options)
}
pub fn set_color_conversion_options(&mut self, options: ColorConversionOptions) {
let inner = self.inner_mut();
options.fill_cc_options(&mut inner.color_conversion_options);
}
#[cfg(feature = "v1_20")]
pub fn alpha_composition_mode(&self) -> AlphaCompositionMode {
let inner = self.inner_ref();
AlphaCompositionMode::from_libheif(inner.color_conversion_options_ext)
}
#[cfg(feature = "v1_20")]
pub fn set_alpha_composition_mode(&mut self, v: AlphaCompositionMode) {
let inner = self.inner_mut();
if inner.color_conversion_options_ext.is_null() {
inner.color_conversion_options_ext =
unsafe { lh::heif_color_conversion_options_ext_alloc() };
}
v.fill_libheif_cc_options_ext(inner.color_conversion_options_ext);
}
#[cfg(feature = "v1_21")]
pub fn ignore_sequence_edit_list(&self) -> bool {
let inner = self.inner_ref();
inner.ignore_sequence_editlist != 0
}
#[cfg(feature = "v1_21")]
pub fn set_ignore_sequence_edit_list(&mut self, v: bool) {
let inner = self.inner_mut();
inner.ignore_sequence_editlist = v as _;
}
#[cfg(feature = "v1_21")]
pub fn output_image_nclx_profile(&self) -> Option<&ColorProfileNCLX> {
self.output_image_nclx_profile.as_ref()
}
#[cfg(feature = "v1_21")]
pub fn set_output_image_nclx_profile(&mut self, v: Option<ColorProfileNCLX>) {
self.output_image_nclx_profile = v;
let profile_ptr = self
.output_image_nclx_profile
.as_ref()
.map(|v| v.inner)
.unwrap_or(ptr::null_mut());
let inner = self.inner_mut();
inner.output_image_nclx_profile = profile_ptr;
}
#[cfg(feature = "v1_21")]
pub fn num_library_threads(&self) -> u32 {
let inner = self.inner_ref();
inner.num_library_threads.max(0) as _
}
#[cfg(feature = "v1_21")]
pub fn set_num_library_threads(&mut self, v: u32) {
let inner = self.inner_mut();
inner.num_library_threads = v.min(i32::MAX as u32) as _;
}
#[cfg(feature = "v1_21")]
pub fn num_codec_threads(&self) -> u32 {
let inner = self.inner_ref();
inner.num_codec_threads.max(0) as _
}
#[cfg(feature = "v1_21")]
pub fn set_num_codec_threads(&mut self, v: u32) {
let inner = self.inner_mut();
inner.num_codec_threads = v.min(i32::MAX as u32) 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)
}
#[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,
}
impl Default for ColorConversionOptions {
fn default() -> Self {
#[allow(unused_mut)]
let mut cc_options = lh::heif_color_conversion_options {
version: 1,
preferred_chroma_downsampling_algorithm: 0,
preferred_chroma_upsampling_algorithm: 0,
only_use_preferred_chroma_algorithm: 0,
};
#[cfg(feature = "v1_19")]
unsafe {
lh::heif_color_conversion_options_set_defaults(&mut cc_options)
};
Self::from_cc_options(&cc_options)
}
}
impl ColorConversionOptions {
pub fn new() -> Self {
Default::default()
}
pub(crate) fn from_cc_options(cc_options: &lh::heif_color_conversion_options) -> Self {
let preferred_chroma_downsampling_algorithm =
ChromaDownsamplingAlgorithm::n(cc_options.preferred_chroma_downsampling_algorithm)
.unwrap_or(ChromaDownsamplingAlgorithm::Average);
let preferred_chroma_upsampling_algorithm =
ChromaUpsamplingAlgorithm::n(cc_options.preferred_chroma_upsampling_algorithm)
.unwrap_or(ChromaUpsamplingAlgorithm::Bilinear);
let only_use_preferred_chroma_algorithm =
cc_options.only_use_preferred_chroma_algorithm != 0;
Self {
preferred_chroma_downsampling_algorithm,
preferred_chroma_upsampling_algorithm,
only_use_preferred_chroma_algorithm,
}
}
pub(crate) fn fill_cc_options(&self, cc_options: &mut lh::heif_color_conversion_options) {
cc_options.preferred_chroma_downsampling_algorithm =
self.preferred_chroma_downsampling_algorithm as _;
cc_options.preferred_chroma_upsampling_algorithm =
self.preferred_chroma_upsampling_algorithm as _;
cc_options.only_use_preferred_chroma_algorithm =
self.only_use_preferred_chroma_algorithm as _;
}
}
#[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()
}
}