Skip to main content

xberg_libheif/
decoder.rs

1use std::ffi::CString;
2use std::fmt::{Debug, Formatter};
3use std::ptr;
4use std::sync::Mutex;
5
6use libheif_sys as lh;
7
8#[cfg(feature = "v1_20")]
9use crate::AlphaCompositionMode;
10use crate::utils::{cstr_to_str, str_to_cstring};
11use crate::{ChromaDownsamplingAlgorithm, ChromaUpsamplingAlgorithm, ColorProfileNCLX, HeifError};
12static DECODER_MUTEX: Mutex<()> = Mutex::new(());
13
14#[derive(Debug)]
15pub struct DecodingOptions {
16    inner: ptr::NonNull<lh::heif_decoding_options>,
17    decoder_id: Option<CString>,
18    #[allow(dead_code)]
19    output_image_nclx_profile: Option<ColorProfileNCLX>,
20}
21
22#[allow(unsafe_code)]
23impl DecodingOptions {
24    pub fn new() -> Option<Self> {
25        let inner_ptr = unsafe { lh::heif_decoding_options_alloc() };
26        ptr::NonNull::new(inner_ptr).map(|inner| Self {
27            inner,
28            decoder_id: None,
29            output_image_nclx_profile: None,
30        })
31    }
32}
33
34#[allow(unsafe_code)]
35impl Drop for DecodingOptions {
36    fn drop(&mut self) {
37        #[cfg(feature = "v1_20")]
38        {
39            let inner_mut = self.inner_mut();
40            if !inner_mut.color_conversion_options_ext.is_null() {
41                unsafe { lh::heif_color_conversion_options_ext_free(inner_mut.color_conversion_options_ext) };
42                inner_mut.color_conversion_options_ext = ptr::null_mut();
43            }
44        }
45        unsafe {
46            lh::heif_decoding_options_free(self.inner.as_ptr());
47        }
48    }
49}
50
51#[allow(unsafe_code)]
52impl DecodingOptions {
53    #[inline(always)]
54    #[allow(unsafe_code)]
55    fn inner_ref(&self) -> &lh::heif_decoding_options {
56        unsafe { self.inner.as_ref() }
57    }
58
59    #[inline(always)]
60    #[allow(unsafe_code)]
61    pub(crate) fn inner_mut(&mut self) -> &mut lh::heif_decoding_options {
62        unsafe { self.inner.as_mut() }
63    }
64
65    #[inline]
66    pub fn version(&self) -> u8 {
67        self.inner_ref().version
68    }
69
70    /// Ignore geometric transformations like cropping, rotation, mirroring.
71    /// Default: false (do not ignore).
72    #[inline]
73    pub fn ignore_transformations(&self) -> bool {
74        self.inner_ref().ignore_transformations != 0
75    }
76
77    #[inline]
78    pub fn set_ignore_transformations(&mut self, enable: bool) {
79        self.inner_mut().ignore_transformations = if enable { 1 } else { 0 }
80    }
81
82    #[inline]
83    pub fn convert_hdr_to_8bit(&self) -> bool {
84        self.inner_ref().convert_hdr_to_8bit != 0
85    }
86
87    #[inline]
88    pub fn set_convert_hdr_to_8bit(&mut self, enable: bool) {
89        self.inner_mut().convert_hdr_to_8bit = if enable { 1 } else { 0 }
90    }
91
92    /// When strict decoding is enabled, an error is returned for invalid input.
93    /// Otherwise, it will try its best and add decoding warnings to
94    /// the decoded `Image`. Default is non-strict.
95    pub fn strict_decoding(&self) -> bool {
96        self.inner_ref().strict_decoding != 0
97    }
98
99    pub fn set_strict_decoding(&mut self, enable: bool) {
100        self.inner_mut().strict_decoding = if enable { 1 } else { 0 }
101    }
102
103    /// ID of the decoder to use for the decoding.
104    /// If set to `None` (default), the highest priority decoder is chosen.
105    /// The priority is defined in the plugin.
106    pub fn decoder_id(&self) -> Option<&str> {
107        cstr_to_str(self.inner_ref().decoder_id)
108    }
109
110    pub fn set_decoder_id(&mut self, decoder_id: Option<&str>) -> Result<(), HeifError> {
111        if let Some(decoder_id) = decoder_id {
112            let c_decoder_id = str_to_cstring(decoder_id, "decoder_id")?;
113            self.inner_mut().decoder_id = c_decoder_id.as_ptr();
114            self.decoder_id = Some(c_decoder_id);
115        } else {
116            self.inner_mut().decoder_id = ptr::null() as _;
117            self.decoder_id = None;
118        }
119        Ok(())
120    }
121
122    pub fn color_conversion_options(&self) -> ColorConversionOptions {
123        let inner = self.inner_ref();
124        ColorConversionOptions::from_cc_options(&inner.color_conversion_options)
125    }
126
127    pub fn set_color_conversion_options(&mut self, options: ColorConversionOptions) {
128        let inner = self.inner_mut();
129        options.fill_cc_options(&mut inner.color_conversion_options);
130    }
131
132    #[cfg(feature = "v1_20")]
133    pub fn alpha_composition_mode(&self) -> AlphaCompositionMode {
134        let inner = self.inner_ref();
135        AlphaCompositionMode::from_libheif(inner.color_conversion_options_ext)
136    }
137
138    #[cfg(feature = "v1_20")]
139    #[allow(unsafe_code)]
140    pub fn set_alpha_composition_mode(&mut self, v: AlphaCompositionMode) {
141        let inner = self.inner_mut();
142        if inner.color_conversion_options_ext.is_null() {
143            inner.color_conversion_options_ext = unsafe { lh::heif_color_conversion_options_ext_alloc() };
144        }
145        v.fill_libheif_cc_options_ext(inner.color_conversion_options_ext);
146    }
147
148    #[cfg(feature = "v1_21")]
149    /// If enabled, it will decode the media timeline,
150    /// ignoring the sequence tracks edit-list.
151    pub fn ignore_sequence_edit_list(&self) -> bool {
152        let inner = self.inner_ref();
153        inner.ignore_sequence_editlist != 0
154    }
155
156    #[cfg(feature = "v1_21")]
157    /// If enabled, it will decode the media timeline,
158    /// ignoring the sequence tracks edit-list.
159    pub fn set_ignore_sequence_edit_list(&mut self, v: bool) {
160        let inner = self.inner_mut();
161        inner.ignore_sequence_editlist = v as _;
162    }
163
164    #[cfg(feature = "v1_21")]
165    pub fn output_image_nclx_profile(&self) -> Option<&ColorProfileNCLX> {
166        self.output_image_nclx_profile.as_ref()
167    }
168
169    #[cfg(feature = "v1_21")]
170    pub fn set_output_image_nclx_profile(&mut self, v: Option<ColorProfileNCLX>) {
171        self.output_image_nclx_profile = v;
172        let profile_ptr = self
173            .output_image_nclx_profile
174            .as_ref()
175            .map(|v| v.inner)
176            .unwrap_or(ptr::null_mut());
177        let inner = self.inner_mut();
178        inner.output_image_nclx_profile = profile_ptr;
179    }
180
181    #[cfg(feature = "v1_21")]
182    /// 0 lets libheif decide.
183    pub fn num_library_threads(&self) -> u32 {
184        let inner = self.inner_ref();
185        inner.num_library_threads.max(0) as _
186    }
187
188    #[cfg(feature = "v1_21")]
189    /// 0 lets libheif decide.
190    pub fn set_num_library_threads(&mut self, v: u32) {
191        let inner = self.inner_mut();
192        inner.num_library_threads = v.min(i32::MAX as u32) as _;
193    }
194
195    #[cfg(feature = "v1_21")]
196    /// 0 = use decoder default
197    pub fn num_codec_threads(&self) -> u32 {
198        let inner = self.inner_ref();
199        inner.num_codec_threads.max(0) as _
200    }
201
202    #[cfg(feature = "v1_21")]
203    /// 0 = use decoder default
204    pub fn set_num_codec_threads(&mut self, v: u32) {
205        let inner = self.inner_mut();
206        inner.num_codec_threads = v.min(i32::MAX as u32) as _;
207    }
208}
209
210/// This function makes sure the decoding options
211/// won't be freed too early.
212pub(crate) fn get_decoding_options_ptr(options: &Option<DecodingOptions>) -> *mut lh::heif_decoding_options {
213    options.as_ref().map(|o| o.inner.as_ptr()).unwrap_or_else(ptr::null_mut)
214}
215
216#[derive(Debug, Copy, Clone)]
217pub struct ColorConversionOptions {
218    pub preferred_chroma_downsampling_algorithm: ChromaDownsamplingAlgorithm,
219    pub preferred_chroma_upsampling_algorithm: ChromaUpsamplingAlgorithm,
220    /// When set to `false`, libheif may also use a different algorithm
221    /// if the preferred one is not available.
222    pub only_use_preferred_chroma_algorithm: bool,
223}
224
225#[allow(unsafe_code)]
226impl Default for ColorConversionOptions {
227    fn default() -> Self {
228        #[allow(unused_mut)]
229        let mut cc_options = lh::heif_color_conversion_options {
230            version: 1,
231            preferred_chroma_downsampling_algorithm: 0,
232            preferred_chroma_upsampling_algorithm: 0,
233            only_use_preferred_chroma_algorithm: 0,
234        };
235        #[cfg(feature = "v1_19")]
236        {
237            unsafe { lh::heif_color_conversion_options_set_defaults(&mut cc_options) };
238        }
239        Self::from_cc_options(&cc_options)
240    }
241}
242
243impl ColorConversionOptions {
244    pub fn new() -> Self {
245        Default::default()
246    }
247
248    pub(crate) fn from_cc_options(cc_options: &lh::heif_color_conversion_options) -> Self {
249        let preferred_chroma_downsampling_algorithm =
250            ChromaDownsamplingAlgorithm::n(cc_options.preferred_chroma_downsampling_algorithm)
251                .unwrap_or(ChromaDownsamplingAlgorithm::Average);
252        let preferred_chroma_upsampling_algorithm =
253            ChromaUpsamplingAlgorithm::n(cc_options.preferred_chroma_upsampling_algorithm)
254                .unwrap_or(ChromaUpsamplingAlgorithm::Bilinear);
255        let only_use_preferred_chroma_algorithm = cc_options.only_use_preferred_chroma_algorithm != 0;
256        Self {
257            preferred_chroma_downsampling_algorithm,
258            preferred_chroma_upsampling_algorithm,
259            only_use_preferred_chroma_algorithm,
260        }
261    }
262
263    pub(crate) fn fill_cc_options(&self, cc_options: &mut lh::heif_color_conversion_options) {
264        cc_options.preferred_chroma_downsampling_algorithm = self.preferred_chroma_downsampling_algorithm as _;
265        cc_options.preferred_chroma_upsampling_algorithm = self.preferred_chroma_upsampling_algorithm as _;
266        cc_options.only_use_preferred_chroma_algorithm = self.only_use_preferred_chroma_algorithm as _;
267    }
268}
269
270#[derive(Copy, Clone)]
271pub struct DecoderDescriptor<'a> {
272    inner: &'a lh::heif_decoder_descriptor,
273}
274
275#[allow(unsafe_code)]
276impl<'a> DecoderDescriptor<'a> {
277    pub(crate) fn new(inner: &'a lh::heif_decoder_descriptor) -> Self {
278        Self { inner }
279    }
280
281    /// A short, symbolic name for identifying the decoder.
282    /// This name should stay constant over different decoder versions.
283    #[allow(unsafe_code)]
284    pub fn id(&self) -> &str {
285        let name = unsafe { lh::heif_decoder_descriptor_get_id_name(self.inner) };
286        cstr_to_str(name).unwrap_or_default()
287    }
288
289    /// A long, descriptive name of the decoder
290    /// (including version information).
291    #[allow(unsafe_code)]
292    pub fn name(&self) -> String {
293        let _lock = DECODER_MUTEX.lock();
294        let name = unsafe { lh::heif_decoder_descriptor_get_name(self.inner) };
295        cstr_to_str(name).unwrap_or_default().to_owned()
296    }
297}
298
299impl<'a> Debug for DecoderDescriptor<'a> {
300    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
301        f.debug_struct("DecoderDescriptor")
302            .field("id", &self.id())
303            .field("name", &self.name())
304            .finish()
305    }
306}