1use std::ffi::CString;
2use std::fmt::{Debug, Formatter};
3use std::ptr;
4use std::sync::Mutex;
5
6use libheif_sys as lh;
7
8use crate::utils::{cstr_to_str, str_to_cstring};
9#[cfg(feature = "v1_20")]
10use crate::AlphaCompositionMode;
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
22impl DecodingOptions {
23 pub fn new() -> Option<Self> {
24 let inner_ptr = unsafe { lh::heif_decoding_options_alloc() };
25 ptr::NonNull::new(inner_ptr).map(|inner| Self {
26 inner,
27 decoder_id: None,
28 output_image_nclx_profile: None,
29 })
30 }
31}
32
33impl Drop for DecodingOptions {
34 fn drop(&mut self) {
35 #[cfg(feature = "v1_20")]
36 {
37 let inner_mut = self.inner_mut();
38 if !inner_mut.color_conversion_options_ext.is_null() {
39 unsafe {
40 lh::heif_color_conversion_options_ext_free(
41 inner_mut.color_conversion_options_ext,
42 )
43 };
44 inner_mut.color_conversion_options_ext = ptr::null_mut();
45 }
46 }
47 unsafe {
48 lh::heif_decoding_options_free(self.inner.as_ptr());
49 }
50 }
51}
52
53impl DecodingOptions {
54 #[inline(always)]
55 fn inner_ref(&self) -> &lh::heif_decoding_options {
56 unsafe { self.inner.as_ref() }
57 }
58
59 #[inline(always)]
60 pub(crate) fn inner_mut(&mut self) -> &mut lh::heif_decoding_options {
61 unsafe { self.inner.as_mut() }
62 }
63
64 #[inline]
65 pub fn version(&self) -> u8 {
66 self.inner_ref().version
67 }
68
69 #[inline]
72 pub fn ignore_transformations(&self) -> bool {
73 self.inner_ref().ignore_transformations != 0
74 }
75
76 #[inline]
77 pub fn set_ignore_transformations(&mut self, enable: bool) {
78 self.inner_mut().ignore_transformations = if enable { 1 } else { 0 }
79 }
80
81 #[inline]
82 pub fn convert_hdr_to_8bit(&self) -> bool {
83 self.inner_ref().convert_hdr_to_8bit != 0
84 }
85
86 #[inline]
87 pub fn set_convert_hdr_to_8bit(&mut self, enable: bool) {
88 self.inner_mut().convert_hdr_to_8bit = if enable { 1 } else { 0 }
89 }
90
91 pub fn strict_decoding(&self) -> bool {
95 self.inner_ref().strict_decoding != 0
96 }
97
98 pub fn set_strict_decoding(&mut self, enable: bool) {
99 self.inner_mut().strict_decoding = if enable { 1 } else { 0 }
100 }
101
102 pub fn decoder_id(&self) -> Option<&str> {
106 cstr_to_str(self.inner_ref().decoder_id)
107 }
108
109 pub fn set_decoder_id(&mut self, decoder_id: Option<&str>) -> Result<(), HeifError> {
110 if let Some(decoder_id) = decoder_id {
111 let c_decoder_id = str_to_cstring(decoder_id, "decoder_id")?;
112 self.inner_mut().decoder_id = c_decoder_id.as_ptr();
113 self.decoder_id = Some(c_decoder_id);
114 } else {
115 self.inner_mut().decoder_id = ptr::null() as _;
116 self.decoder_id = None;
117 }
118 Ok(())
119 }
120
121 pub fn color_conversion_options(&self) -> ColorConversionOptions {
122 let inner = self.inner_ref();
123 ColorConversionOptions::from_cc_options(&inner.color_conversion_options)
124 }
125
126 pub fn set_color_conversion_options(&mut self, options: ColorConversionOptions) {
127 let inner = self.inner_mut();
128 options.fill_cc_options(&mut inner.color_conversion_options);
129 }
130
131 #[cfg(feature = "v1_20")]
132 pub fn alpha_composition_mode(&self) -> AlphaCompositionMode {
133 let inner = self.inner_ref();
134 AlphaCompositionMode::from_libheif(inner.color_conversion_options_ext)
135 }
136
137 #[cfg(feature = "v1_20")]
138 pub fn set_alpha_composition_mode(&mut self, v: AlphaCompositionMode) {
139 let inner = self.inner_mut();
140 if inner.color_conversion_options_ext.is_null() {
141 inner.color_conversion_options_ext =
142 unsafe { lh::heif_color_conversion_options_ext_alloc() };
143 }
144 v.fill_libheif_cc_options_ext(inner.color_conversion_options_ext);
145 }
146
147 #[cfg(feature = "v1_21")]
148 pub fn ignore_sequence_edit_list(&self) -> bool {
151 let inner = self.inner_ref();
152 inner.ignore_sequence_editlist != 0
153 }
154
155 #[cfg(feature = "v1_21")]
156 pub fn set_ignore_sequence_edit_list(&mut self, v: bool) {
159 let inner = self.inner_mut();
160 inner.ignore_sequence_editlist = v as _;
161 }
162
163 #[cfg(feature = "v1_21")]
164 pub fn output_image_nclx_profile(&self) -> Option<&ColorProfileNCLX> {
165 self.output_image_nclx_profile.as_ref()
166 }
167
168 #[cfg(feature = "v1_21")]
169 pub fn set_output_image_nclx_profile(&mut self, v: Option<ColorProfileNCLX>) {
170 self.output_image_nclx_profile = v;
171 let profile_ptr = self
172 .output_image_nclx_profile
173 .as_ref()
174 .map(|v| v.inner)
175 .unwrap_or(ptr::null_mut());
176 let inner = self.inner_mut();
177 inner.output_image_nclx_profile = profile_ptr;
178 }
179
180 #[cfg(feature = "v1_21")]
181 pub fn num_library_threads(&self) -> u32 {
183 let inner = self.inner_ref();
184 inner.num_library_threads.max(0) as _
185 }
186
187 #[cfg(feature = "v1_21")]
188 pub fn set_num_library_threads(&mut self, v: u32) {
190 let inner = self.inner_mut();
191 inner.num_library_threads = v.min(i32::MAX as u32) as _;
192 }
193
194 #[cfg(feature = "v1_21")]
195 pub fn num_codec_threads(&self) -> u32 {
197 let inner = self.inner_ref();
198 inner.num_codec_threads.max(0) as _
199 }
200
201 #[cfg(feature = "v1_21")]
202 pub fn set_num_codec_threads(&mut self, v: u32) {
204 let inner = self.inner_mut();
205 inner.num_codec_threads = v.min(i32::MAX as u32) as _;
206 }
207
208 #[cfg(feature = "v1_23")]
209 pub fn autocorrect_broken_input(&self) -> bool {
215 let inner = self.inner_ref();
216 inner.autocorrect_broken_input > 0
217 }
218
219 #[cfg(feature = "v1_23")]
220 pub fn set_autocorrect_broken_input(&mut self, v: bool) {
221 let inner = self.inner_mut();
222 inner.autocorrect_broken_input = if v { 1 } else { 0 };
223 }
224
225 #[cfg(feature = "v1_23")]
226 pub fn output_image_nclx_profile_passthrough(&self) -> bool {
248 let inner = self.inner_ref();
249 inner.output_image_nclx_profile_passthrough > 0
250 }
251
252 #[cfg(feature = "v1_23")]
253 pub fn set_output_image_nclx_profile_passthrough(&mut self, v: bool) {
254 let inner = self.inner_mut();
255 inner.output_image_nclx_profile_passthrough = if v { 1 } else { 0 };
256 }
257}
258
259pub(crate) fn get_decoding_options_ptr(
262 options: &Option<DecodingOptions>,
263) -> *mut lh::heif_decoding_options {
264 options
265 .as_ref()
266 .map(|o| o.inner.as_ptr())
267 .unwrap_or_else(ptr::null_mut)
268}
269
270#[derive(Debug, Copy, Clone)]
271pub struct ColorConversionOptions {
272 pub preferred_chroma_downsampling_algorithm: ChromaDownsamplingAlgorithm,
273 pub preferred_chroma_upsampling_algorithm: ChromaUpsamplingAlgorithm,
274 pub only_use_preferred_chroma_algorithm: bool,
277}
278
279impl Default for ColorConversionOptions {
280 fn default() -> Self {
281 #[allow(unused_mut)]
282 let mut cc_options = lh::heif_color_conversion_options {
283 version: 1,
284 preferred_chroma_downsampling_algorithm: 0,
285 preferred_chroma_upsampling_algorithm: 0,
286 only_use_preferred_chroma_algorithm: 0,
287 };
288 #[cfg(feature = "v1_19")]
289 unsafe {
290 lh::heif_color_conversion_options_set_defaults(&mut cc_options)
291 };
292 Self::from_cc_options(&cc_options)
293 }
294}
295
296impl ColorConversionOptions {
297 pub fn new() -> Self {
298 Default::default()
299 }
300
301 pub(crate) fn from_cc_options(cc_options: &lh::heif_color_conversion_options) -> Self {
302 let preferred_chroma_downsampling_algorithm =
303 ChromaDownsamplingAlgorithm::n(cc_options.preferred_chroma_downsampling_algorithm)
304 .unwrap_or(ChromaDownsamplingAlgorithm::Average);
305 let preferred_chroma_upsampling_algorithm =
306 ChromaUpsamplingAlgorithm::n(cc_options.preferred_chroma_upsampling_algorithm)
307 .unwrap_or(ChromaUpsamplingAlgorithm::Bilinear);
308 let only_use_preferred_chroma_algorithm =
309 cc_options.only_use_preferred_chroma_algorithm != 0;
310 Self {
311 preferred_chroma_downsampling_algorithm,
312 preferred_chroma_upsampling_algorithm,
313 only_use_preferred_chroma_algorithm,
314 }
315 }
316
317 pub(crate) fn fill_cc_options(&self, cc_options: &mut lh::heif_color_conversion_options) {
318 cc_options.preferred_chroma_downsampling_algorithm =
319 self.preferred_chroma_downsampling_algorithm as _;
320 cc_options.preferred_chroma_upsampling_algorithm =
321 self.preferred_chroma_upsampling_algorithm as _;
322 cc_options.only_use_preferred_chroma_algorithm =
323 self.only_use_preferred_chroma_algorithm as _;
324 }
325}
326
327#[derive(Copy, Clone)]
328pub struct DecoderDescriptor<'a> {
329 inner: &'a lh::heif_decoder_descriptor,
330}
331
332impl<'a> DecoderDescriptor<'a> {
333 pub(crate) fn new(inner: &'a lh::heif_decoder_descriptor) -> Self {
334 Self { inner }
335 }
336
337 pub fn id(&self) -> &str {
340 let name = unsafe { lh::heif_decoder_descriptor_get_id_name(self.inner) };
341 cstr_to_str(name).unwrap_or_default()
342 }
343
344 pub fn name(&self) -> String {
347 let _lock = DECODER_MUTEX.lock();
350 let name = unsafe { lh::heif_decoder_descriptor_get_name(self.inner) };
351 cstr_to_str(name).unwrap_or_default().to_owned()
352 }
353}
354
355impl<'a> Debug for DecoderDescriptor<'a> {
356 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
357 f.debug_struct("DecoderDescriptor")
358 .field("id", &self.id())
359 .field("name", &self.name())
360 .finish()
361 }
362}