1use std::collections::HashMap;
2use std::ffi::CString;
3use std::fmt::{Debug, Formatter};
4use std::marker::PhantomData;
5use std::ptr;
6use std::sync::Mutex;
7
8use libheif_sys as lh;
9
10use crate::utils::cstr_to_str;
11use crate::{
12 ChromaDownsamplingAlgorithm, ChromaUpsamplingAlgorithm, ColorConversionOptions, HeifError,
13 HeifErrorCode, HeifErrorSubCode, ImageOrientation, Result,
14};
15
16static ENCODER_MUTEX: Mutex<()> = Mutex::new(());
17
18#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, enumn::N)]
19#[non_exhaustive]
20#[repr(C)]
21pub enum CompressionFormat {
22 Undefined = lh::heif_compression_format_heif_compression_undefined as _,
24 Hevc = lh::heif_compression_format_heif_compression_HEVC as _,
26 Avc = lh::heif_compression_format_heif_compression_AVC as _,
28 Jpeg = lh::heif_compression_format_heif_compression_JPEG as _,
30 Av1 = lh::heif_compression_format_heif_compression_AV1 as _,
32 Vvc = lh::heif_compression_format_heif_compression_VVC as _,
34 Evc = lh::heif_compression_format_heif_compression_EVC as _,
36 Jpeg2000 = lh::heif_compression_format_heif_compression_JPEG2000 as _,
38 Uncompressed = lh::heif_compression_format_heif_compression_uncompressed as _,
40 Mask = lh::heif_compression_format_heif_compression_mask as _,
42 #[cfg(feature = "v1_18")]
44 HtJ2k = lh::heif_compression_format_heif_compression_HTJ2K as _,
45}
46
47#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, enumn::N)]
48#[repr(C)]
49pub enum EncoderParameterType {
50 Int = lh::heif_encoder_parameter_type_heif_encoder_parameter_type_integer as _,
51 Bool = lh::heif_encoder_parameter_type_heif_encoder_parameter_type_boolean as _,
52 String = lh::heif_encoder_parameter_type_heif_encoder_parameter_type_string as _,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Hash)]
56pub enum EncoderParameterValue {
57 Int(i32),
58 Bool(bool),
59 String(String),
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63pub enum EncoderQuality {
64 LossLess,
65 Lossy(u8),
68}
69
70pub type EncoderParametersTypes = HashMap<String, EncoderParameterType>;
71
72pub struct Encoder<'a> {
73 pub(crate) inner: *mut lh::heif_encoder,
74 pub(crate) parameters_types: EncoderParametersTypes,
75 phantom: PhantomData<&'a mut lh::heif_encoder>,
76}
77
78impl<'a> Encoder<'a> {
79 pub(crate) fn new(c_encoder: &'a mut lh::heif_encoder) -> Result<Self> {
80 let parameters_types = parameters_types(c_encoder)?;
81 Ok(Self {
82 inner: c_encoder,
83 parameters_types,
84 phantom: PhantomData::default(),
85 })
86 }
87}
88
89impl<'a> Drop for Encoder<'a> {
90 fn drop(&mut self) {
91 unsafe { lh::heif_encoder_release(self.inner) };
92 }
93}
94
95impl<'a> Encoder<'a> {
96 pub fn name(&self) -> String {
98 let _lock = ENCODER_MUTEX.lock();
101 let res = unsafe { lh::heif_encoder_get_name(self.inner) };
102 cstr_to_str(res).unwrap_or("").to_owned()
103 }
104
105 pub fn set_quality(&mut self, quality: EncoderQuality) -> Result<()> {
106 let err = match quality {
107 EncoderQuality::LossLess => unsafe { lh::heif_encoder_set_lossless(self.inner, 1) },
108 EncoderQuality::Lossy(value) => unsafe {
109 let middle_err = lh::heif_encoder_set_lossless(self.inner, 0);
110 HeifError::from_heif_error(middle_err)?;
111 lh::heif_encoder_set_lossy_quality(self.inner, i32::from(value))
112 },
113 };
114 HeifError::from_heif_error(err)
115 }
116
117 fn parameter_value(
118 &self,
119 name: &str,
120 parameter_type: EncoderParameterType,
121 ) -> Result<EncoderParameterValue> {
122 let c_param_name = CString::new(name).unwrap();
123 let param_value = match parameter_type {
124 EncoderParameterType::Int => {
125 let mut value = 0;
126 let err = unsafe {
127 lh::heif_encoder_get_parameter_integer(
128 self.inner,
129 c_param_name.as_ptr(),
130 &mut value as _,
131 )
132 };
133 HeifError::from_heif_error(err)?;
134 EncoderParameterValue::Int(value)
135 }
136 EncoderParameterType::Bool => {
137 let mut value = 0;
138 let err = unsafe {
139 lh::heif_encoder_get_parameter_boolean(
140 self.inner,
141 c_param_name.as_ptr(),
142 &mut value as _,
143 )
144 };
145 HeifError::from_heif_error(err)?;
146 EncoderParameterValue::Bool(value > 0)
147 }
148 EncoderParameterType::String => {
149 let value: Vec<u8> = vec![0; 51];
150 let err = unsafe {
151 lh::heif_encoder_get_parameter_string(
152 self.inner,
153 c_param_name.as_ptr(),
154 value.as_ptr() as _,
155 50,
156 )
157 };
158 HeifError::from_heif_error(err)?;
159 EncoderParameterValue::String(
160 cstr_to_str(value.as_ptr() as _).unwrap_or("").to_string(),
161 )
162 }
163 };
164
165 Ok(param_value)
166 }
167
168 pub fn parameters_names(&self) -> Vec<String> {
169 self.parameters_types.keys().cloned().collect()
170 }
171
172 pub fn parameter(&self, name: &str) -> Result<Option<EncoderParameterValue>> {
174 match self.parameters_types.get(name) {
175 Some(param_type) => {
176 let value = self.parameter_value(name, *param_type)?;
177 Ok(Some(value))
178 }
179 None => Ok(None),
180 }
181 }
182
183 pub fn set_parameter_value(&self, name: &str, value: EncoderParameterValue) -> Result<()> {
185 let c_param_name = CString::new(name).unwrap();
186 let err = match value {
187 EncoderParameterValue::Bool(v) => unsafe {
188 lh::heif_encoder_set_parameter_boolean(self.inner, c_param_name.as_ptr(), v.into())
189 },
190 EncoderParameterValue::Int(v) => unsafe {
191 lh::heif_encoder_set_parameter_integer(self.inner, c_param_name.as_ptr(), v)
192 },
193 EncoderParameterValue::String(v) => unsafe {
194 let c_param_value = CString::new(v).unwrap();
195 lh::heif_encoder_set_parameter_string(
196 self.inner,
197 c_param_name.as_ptr(),
198 c_param_value.as_ptr(),
199 )
200 },
201 };
202 HeifError::from_heif_error(err)?;
203 Ok(())
204 }
205}
206
207fn parameters_types(c_encoder: &mut lh::heif_encoder) -> Result<EncoderParametersTypes> {
208 let mut res = EncoderParametersTypes::new();
209 unsafe {
210 let mut param_pointers = lh::heif_encoder_list_parameters(c_encoder);
211 if !param_pointers.is_null() {
212 while let Some(raw_param) = (*param_pointers).as_ref() {
213 let c_param_type = lh::heif_encoder_parameter_get_type(raw_param);
214 let param_type = match EncoderParameterType::n(c_param_type) {
215 Some(res) => res,
216 None => {
217 return Err(HeifError {
218 code: HeifErrorCode::EncoderPluginError,
219 sub_code: HeifErrorSubCode::UnsupportedParameter,
220 message: format!("{} is unknown type of parameter", c_param_type),
221 });
222 }
223 };
224 let c_param_name = lh::heif_encoder_parameter_get_name(raw_param);
225 let name = cstr_to_str(c_param_name).unwrap_or("").to_string();
226 res.insert(name, param_type);
227 param_pointers = param_pointers.offset(1);
228 }
229 }
230 }
231 Ok(res)
232}
233
234#[derive(Debug)]
235pub struct EncodingOptions {
236 inner: ptr::NonNull<lh::heif_encoding_options>,
237}
238
239impl EncodingOptions {
240 pub fn new() -> Result<Self> {
241 let inner_ptr = unsafe { lh::heif_encoding_options_alloc() };
242 match ptr::NonNull::new(inner_ptr) {
243 Some(inner) => Ok(Self { inner }),
244 None => Err(HeifError {
245 code: HeifErrorCode::MemoryAllocationError,
246 sub_code: HeifErrorSubCode::Unspecified,
247 message: Default::default(),
248 }),
249 }
250 }
251}
252
253impl Default for EncodingOptions {
254 fn default() -> Self {
255 Self::new().expect("heif_encoding_options_alloc() returns a null pointer")
256 }
257}
258
259impl Drop for EncodingOptions {
260 fn drop(&mut self) {
261 unsafe {
262 lh::heif_encoding_options_free(self.inner.as_ptr());
263 }
264 }
265}
266
267impl EncodingOptions {
268 #[inline(always)]
269 fn inner_ref(&self) -> &lh::heif_encoding_options {
270 unsafe { self.inner.as_ref() }
271 }
272
273 #[inline(always)]
274 fn inner_mut(&mut self) -> &mut lh::heif_encoding_options {
275 unsafe { self.inner.as_mut() }
276 }
277
278 #[inline]
279 pub fn version(&self) -> u8 {
280 self.inner_ref().version
281 }
282
283 #[inline]
284 pub fn save_alpha_channel(&self) -> bool {
285 self.inner_ref().save_alpha_channel != 0
286 }
287
288 #[inline]
289 pub fn set_save_alpha_channel(&mut self, enable: bool) {
290 self.inner_mut().save_alpha_channel = if enable { 1 } else { 0 };
291 }
292
293 #[inline]
294 pub fn mac_os_compatibility_workaround(&self) -> bool {
295 self.inner_ref().macOS_compatibility_workaround != 0
296 }
297
298 #[inline]
299 pub fn set_mac_os_compatibility_workaround(&mut self, enable: bool) {
300 self.inner_mut().macOS_compatibility_workaround = if enable { 1 } else { 0 };
301 }
302
303 #[inline]
304 pub fn save_two_colr_boxes_when_icc_and_nclx_available(&self) -> bool {
305 self.inner_ref()
306 .save_two_colr_boxes_when_ICC_and_nclx_available
307 != 0
308 }
309
310 #[inline]
311 pub fn set_save_two_colr_boxes_when_icc_and_nclx_available(&mut self, enable: bool) {
312 self.inner_mut()
313 .save_two_colr_boxes_when_ICC_and_nclx_available = if enable { 1 } else { 0 };
314 }
315
316 #[inline]
317 pub fn mac_os_compatibility_workaround_no_nclx_profile(&self) -> bool {
318 self.inner_ref()
319 .macOS_compatibility_workaround_no_nclx_profile
320 != 0
321 }
322
323 #[inline]
324 pub fn set_mac_os_compatibility_workaround_no_nclx_profile(&mut self, enable: bool) {
325 self.inner_mut()
326 .macOS_compatibility_workaround_no_nclx_profile = if enable { 1 } else { 0 };
327 }
328
329 #[inline]
330 pub fn image_orientation(&self) -> ImageOrientation {
331 let orientation = self.inner_ref().image_orientation;
332 ImageOrientation::n(orientation).unwrap_or(ImageOrientation::Normal)
333 }
334
335 #[inline]
336 pub fn set_image_orientation(&mut self, orientation: ImageOrientation) {
337 self.inner_mut().image_orientation = orientation as _;
338 }
339
340 pub fn color_conversion_options(&self) -> ColorConversionOptions {
341 let lh_options = self.inner_ref().color_conversion_options;
342 ColorConversionOptions {
343 preferred_chroma_downsampling_algorithm: ChromaDownsamplingAlgorithm::n(
344 lh_options.preferred_chroma_downsampling_algorithm,
345 )
346 .unwrap_or(ChromaDownsamplingAlgorithm::Average),
347 preferred_chroma_upsampling_algorithm: ChromaUpsamplingAlgorithm::n(
348 lh_options.preferred_chroma_upsampling_algorithm,
349 )
350 .unwrap_or(ChromaUpsamplingAlgorithm::Bilinear),
351 only_use_preferred_chroma_algorithm: lh_options.only_use_preferred_chroma_algorithm
352 != 0,
353 }
354 }
355
356 pub fn set_color_conversion_options(&mut self, options: ColorConversionOptions) {
357 let lh_options = &mut self.inner_mut().color_conversion_options;
358 lh_options.preferred_chroma_downsampling_algorithm =
359 options.preferred_chroma_downsampling_algorithm as _;
360 lh_options.preferred_chroma_upsampling_algorithm =
361 options.preferred_chroma_upsampling_algorithm as _;
362 lh_options.only_use_preferred_chroma_algorithm =
363 options.only_use_preferred_chroma_algorithm as _;
364 }
365}
366
367pub(crate) fn get_encoding_options_ptr(
370 options: &Option<EncodingOptions>,
371) -> *mut lh::heif_encoding_options {
372 options
373 .as_ref()
374 .map(|o| o.inner.as_ptr())
375 .unwrap_or_else(ptr::null_mut)
376}
377
378#[derive(Copy, Clone)]
379pub struct EncoderDescriptor<'a> {
380 pub(crate) inner: &'a lh::heif_encoder_descriptor,
381}
382
383impl<'a> EncoderDescriptor<'a> {
384 pub(crate) fn new(inner: &'a lh::heif_encoder_descriptor) -> Self {
385 Self { inner }
386 }
387
388 pub fn id(&self) -> &str {
391 let name = unsafe { lh::heif_encoder_descriptor_get_id_name(self.inner) };
392 cstr_to_str(name).unwrap_or_default()
393 }
394
395 pub fn name(&self) -> String {
398 let _lock = ENCODER_MUTEX.lock();
401 let name = unsafe { lh::heif_encoder_descriptor_get_name(self.inner) };
402 cstr_to_str(name).unwrap_or_default().to_owned()
403 }
404
405 pub fn compression_format(&self) -> CompressionFormat {
406 let c_format = unsafe { lh::heif_encoder_descriptor_get_compression_format(self.inner) };
407 match CompressionFormat::n(c_format) {
408 Some(res) => res,
409 None => CompressionFormat::Undefined,
410 }
411 }
412
413 pub fn supports_lossy_compression(&self) -> bool {
414 unsafe { lh::heif_encoder_descriptor_supports_lossy_compression(self.inner) != 0 }
415 }
416
417 pub fn supports_lossless_compression(&self) -> bool {
418 unsafe { lh::heif_encoder_descriptor_supports_lossless_compression(self.inner) != 0 }
419 }
420}
421
422impl<'a> Debug for EncoderDescriptor<'a> {
423 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
424 f.debug_struct("EncoderDescriptor")
425 .field("id", &self.id())
426 .field("name", &self.name())
427 .finish()
428 }
429}