kreuzberg_libheif/
decoder.rs1use 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() };
27 ptr::NonNull::new(inner_ptr).map(|inner| Self {
28 inner,
29 decoder_id: None,
30 output_image_nclx_profile: None,
31 })
32 }
33}
34
35#[allow(unsafe_code)]
36impl Drop for DecodingOptions {
37 fn drop(&mut self) {
38 #[cfg(feature = "v1_20")]
39 {
40 let inner_mut = self.inner_mut();
41 if !inner_mut.color_conversion_options_ext.is_null() {
42 unsafe { lh::heif_color_conversion_options_ext_free(inner_mut.color_conversion_options_ext) };
44 inner_mut.color_conversion_options_ext = ptr::null_mut();
45 }
46 }
47 unsafe {
49 lh::heif_decoding_options_free(self.inner.as_ptr());
50 }
51 }
52}
53
54#[allow(unsafe_code)]
55impl DecodingOptions {
56 #[inline(always)]
57 #[allow(unsafe_code)]
58 fn inner_ref(&self) -> &lh::heif_decoding_options {
59 unsafe { self.inner.as_ref() }
61 }
62
63 #[inline(always)]
64 #[allow(unsafe_code)]
65 pub(crate) fn inner_mut(&mut self) -> &mut lh::heif_decoding_options {
66 unsafe { self.inner.as_mut() }
68 }
69
70 #[inline]
71 pub fn version(&self) -> u8 {
72 self.inner_ref().version
73 }
74
75 #[inline]
78 pub fn ignore_transformations(&self) -> bool {
79 self.inner_ref().ignore_transformations != 0
80 }
81
82 #[inline]
83 pub fn set_ignore_transformations(&mut self, enable: bool) {
84 self.inner_mut().ignore_transformations = if enable { 1 } else { 0 }
85 }
86
87 #[inline]
88 pub fn convert_hdr_to_8bit(&self) -> bool {
89 self.inner_ref().convert_hdr_to_8bit != 0
90 }
91
92 #[inline]
93 pub fn set_convert_hdr_to_8bit(&mut self, enable: bool) {
94 self.inner_mut().convert_hdr_to_8bit = if enable { 1 } else { 0 }
95 }
96
97 pub fn strict_decoding(&self) -> bool {
101 self.inner_ref().strict_decoding != 0
102 }
103
104 pub fn set_strict_decoding(&mut self, enable: bool) {
105 self.inner_mut().strict_decoding = if enable { 1 } else { 0 }
106 }
107
108 pub fn decoder_id(&self) -> Option<&str> {
112 cstr_to_str(self.inner_ref().decoder_id)
113 }
114
115 pub fn set_decoder_id(&mut self, decoder_id: Option<&str>) -> Result<(), HeifError> {
116 if let Some(decoder_id) = decoder_id {
117 let c_decoder_id = str_to_cstring(decoder_id, "decoder_id")?;
118 self.inner_mut().decoder_id = c_decoder_id.as_ptr();
119 self.decoder_id = Some(c_decoder_id);
120 } else {
121 self.inner_mut().decoder_id = ptr::null() as _;
122 self.decoder_id = None;
123 }
124 Ok(())
125 }
126
127 pub fn color_conversion_options(&self) -> ColorConversionOptions {
128 let inner = self.inner_ref();
129 ColorConversionOptions::from_cc_options(&inner.color_conversion_options)
130 }
131
132 pub fn set_color_conversion_options(&mut self, options: ColorConversionOptions) {
133 let inner = self.inner_mut();
134 options.fill_cc_options(&mut inner.color_conversion_options);
135 }
136
137 #[cfg(feature = "v1_20")]
138 pub fn alpha_composition_mode(&self) -> AlphaCompositionMode {
139 let inner = self.inner_ref();
140 AlphaCompositionMode::from_libheif(inner.color_conversion_options_ext)
141 }
142
143 #[cfg(feature = "v1_20")]
144 #[allow(unsafe_code)]
145 pub fn set_alpha_composition_mode(&mut self, v: AlphaCompositionMode) {
146 let inner = self.inner_mut();
147 if inner.color_conversion_options_ext.is_null() {
148 inner.color_conversion_options_ext = unsafe { lh::heif_color_conversion_options_ext_alloc() };
150 }
151 v.fill_libheif_cc_options_ext(inner.color_conversion_options_ext);
152 }
153
154 #[cfg(feature = "v1_21")]
155 pub fn ignore_sequence_edit_list(&self) -> bool {
158 let inner = self.inner_ref();
159 inner.ignore_sequence_editlist != 0
160 }
161
162 #[cfg(feature = "v1_21")]
163 pub fn set_ignore_sequence_edit_list(&mut self, v: bool) {
166 let inner = self.inner_mut();
167 inner.ignore_sequence_editlist = v as _;
168 }
169
170 #[cfg(feature = "v1_21")]
171 pub fn output_image_nclx_profile(&self) -> Option<&ColorProfileNCLX> {
172 self.output_image_nclx_profile.as_ref()
173 }
174
175 #[cfg(feature = "v1_21")]
176 pub fn set_output_image_nclx_profile(&mut self, v: Option<ColorProfileNCLX>) {
177 self.output_image_nclx_profile = v;
178 let profile_ptr = self
179 .output_image_nclx_profile
180 .as_ref()
181 .map(|v| v.inner)
182 .unwrap_or(ptr::null_mut());
183 let inner = self.inner_mut();
184 inner.output_image_nclx_profile = profile_ptr;
185 }
186
187 #[cfg(feature = "v1_21")]
188 pub fn num_library_threads(&self) -> u32 {
190 let inner = self.inner_ref();
191 inner.num_library_threads.max(0) as _
192 }
193
194 #[cfg(feature = "v1_21")]
195 pub fn set_num_library_threads(&mut self, v: u32) {
197 let inner = self.inner_mut();
198 inner.num_library_threads = v.min(i32::MAX as u32) as _;
199 }
200
201 #[cfg(feature = "v1_21")]
202 pub fn num_codec_threads(&self) -> u32 {
204 let inner = self.inner_ref();
205 inner.num_codec_threads.max(0) as _
206 }
207
208 #[cfg(feature = "v1_21")]
209 pub fn set_num_codec_threads(&mut self, v: u32) {
211 let inner = self.inner_mut();
212 inner.num_codec_threads = v.min(i32::MAX as u32) as _;
213 }
214}
215
216pub(crate) fn get_decoding_options_ptr(options: &Option<DecodingOptions>) -> *mut lh::heif_decoding_options {
219 options.as_ref().map(|o| o.inner.as_ptr()).unwrap_or_else(ptr::null_mut)
220}
221
222#[derive(Debug, Copy, Clone)]
223pub struct ColorConversionOptions {
224 pub preferred_chroma_downsampling_algorithm: ChromaDownsamplingAlgorithm,
225 pub preferred_chroma_upsampling_algorithm: ChromaUpsamplingAlgorithm,
226 pub only_use_preferred_chroma_algorithm: bool,
229}
230
231#[allow(unsafe_code)]
232impl Default for ColorConversionOptions {
233 fn default() -> Self {
234 #[allow(unused_mut)]
235 let mut cc_options = lh::heif_color_conversion_options {
236 version: 1,
237 preferred_chroma_downsampling_algorithm: 0,
238 preferred_chroma_upsampling_algorithm: 0,
239 only_use_preferred_chroma_algorithm: 0,
240 };
241 #[cfg(feature = "v1_19")]
242 {
243 unsafe { lh::heif_color_conversion_options_set_defaults(&mut cc_options) };
245 }
246 Self::from_cc_options(&cc_options)
247 }
248}
249
250impl ColorConversionOptions {
251 pub fn new() -> Self {
252 Default::default()
253 }
254
255 pub(crate) fn from_cc_options(cc_options: &lh::heif_color_conversion_options) -> Self {
256 let preferred_chroma_downsampling_algorithm =
257 ChromaDownsamplingAlgorithm::n(cc_options.preferred_chroma_downsampling_algorithm)
258 .unwrap_or(ChromaDownsamplingAlgorithm::Average);
259 let preferred_chroma_upsampling_algorithm =
260 ChromaUpsamplingAlgorithm::n(cc_options.preferred_chroma_upsampling_algorithm)
261 .unwrap_or(ChromaUpsamplingAlgorithm::Bilinear);
262 let only_use_preferred_chroma_algorithm = cc_options.only_use_preferred_chroma_algorithm != 0;
263 Self {
264 preferred_chroma_downsampling_algorithm,
265 preferred_chroma_upsampling_algorithm,
266 only_use_preferred_chroma_algorithm,
267 }
268 }
269
270 pub(crate) fn fill_cc_options(&self, cc_options: &mut lh::heif_color_conversion_options) {
271 cc_options.preferred_chroma_downsampling_algorithm = self.preferred_chroma_downsampling_algorithm as _;
272 cc_options.preferred_chroma_upsampling_algorithm = self.preferred_chroma_upsampling_algorithm as _;
273 cc_options.only_use_preferred_chroma_algorithm = self.only_use_preferred_chroma_algorithm as _;
274 }
275}
276
277#[derive(Copy, Clone)]
278pub struct DecoderDescriptor<'a> {
279 inner: &'a lh::heif_decoder_descriptor,
280}
281
282#[allow(unsafe_code)]
283impl<'a> DecoderDescriptor<'a> {
284 pub(crate) fn new(inner: &'a lh::heif_decoder_descriptor) -> Self {
285 Self { inner }
286 }
287
288 #[allow(unsafe_code)]
291 pub fn id(&self) -> &str {
292 let name = unsafe { lh::heif_decoder_descriptor_get_id_name(self.inner) };
294 cstr_to_str(name).unwrap_or_default()
295 }
296
297 #[allow(unsafe_code)]
300 pub fn name(&self) -> String {
301 let _lock = DECODER_MUTEX.lock();
304 let name = unsafe { lh::heif_decoder_descriptor_get_name(self.inner) };
306 cstr_to_str(name).unwrap_or_default().to_owned()
307 }
308}
309
310impl<'a> Debug for DecoderDescriptor<'a> {
311 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
312 f.debug_struct("DecoderDescriptor")
313 .field("id", &self.id())
314 .field("name", &self.name())
315 .finish()
316 }
317}