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 #[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 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 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 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 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 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 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 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 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
210pub(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 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 #[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 #[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}