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