libheif_rs/
heif.rs

1use std::ffi::CString;
2use std::path::Path;
3use std::ptr;
4
5use libheif_sys as lh;
6
7use crate::decoder::get_decoding_options_ptr;
8use crate::utils::path_to_cstring;
9use crate::{
10    ColorSpace, CompressionFormat, DecoderDescriptor, DecodingOptions, Encoder, EncoderDescriptor,
11    HeifError, Image, ImageHandle, Result,
12};
13
14/// Guard structure used for `libheif` initialization, working with plugins,
15/// getting encoders, decode images, etc.
16///
17/// You may use one instance of this structure for all parts of your code.
18/// Methods of the structure use static variables inside `libheif`.
19/// So any changes that they make in internals of `libheif`
20/// will have side effect to all instances of the structure.
21///
22/// For example, if you load some plugins by one `LibHeif` instance,
23/// all those plugins will be available to all other instances.
24pub struct LibHeif(());
25
26impl LibHeif {
27    pub fn new() -> Self {
28        let mut init_params = lh::heif_init_params { version: 0 };
29        unsafe {
30            lh::heif_init(&mut init_params as _);
31        }
32        Self(())
33    }
34
35    /// Use this method if you need to make sure that external plugins
36    /// have loaded successfully.
37    ///
38    /// Default paths of directories with external plugins may be
39    /// changed with help of environment variable `LIBHEIF_PLUGIN_PATH`.
40    pub fn new_checked() -> Result<Self> {
41        let mut init_params = lh::heif_init_params { version: 0 };
42        let error = unsafe { lh::heif_init(&mut init_params as _) };
43        HeifError::from_heif_error(error)?;
44        Ok(Self(()))
45    }
46}
47
48impl Drop for LibHeif {
49    fn drop(&mut self) {
50        unsafe {
51            lh::heif_deinit();
52        }
53    }
54}
55
56impl Default for LibHeif {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl LibHeif {
63    /// Version of linked `libheif` as an array of numbers in format:
64    ///
65    /// `[<major>, <minor>, <maintenance>]`
66    pub fn version(&self) -> [u8; 3] {
67        // Numeric version of linked libheif library, encoded as 0xHHMMLL00 = HH.MM.LL.
68        let version: u32 = unsafe { lh::heif_get_version_number() };
69        let parts = version.to_be_bytes();
70        [parts[0], parts[1], parts[2]]
71    }
72
73    /// Load all plugins from given path of directory.
74    ///
75    /// Returns number of loaded plugins.
76    pub fn load_plugins(&self, dir_path: impl AsRef<Path>) -> Result<usize> {
77        self._load_plugins(dir_path.as_ref())
78    }
79
80    // TODO: Consider using 'momo' crate in the future
81    fn _load_plugins(&self, dir_path: &Path) -> Result<usize> {
82        let dir_path = path_to_cstring(dir_path);
83        let mut plugins_loaded: libc::c_int = 0;
84        let err = unsafe {
85            lh::heif_load_plugins(
86                dir_path.as_ptr(),
87                ptr::null_mut() as _,
88                &mut plugins_loaded as _,
89                0,
90            )
91        };
92        HeifError::from_heif_error(err)?;
93        Ok(plugins_loaded as usize)
94    }
95
96    /// Decode an image handle into the actual pixel image and also carry out
97    /// all geometric transformations specified in the HEIF file (rotation, cropping, mirroring).
98    ///
99    /// If `color_space` is set to [`ColorSpace::Undefined`],
100    /// respectively, the original colorspace is taken.
101    pub fn decode(
102        &self,
103        image_handle: &ImageHandle,
104        color_space: ColorSpace,
105        decoding_options: Option<DecodingOptions>,
106    ) -> Result<Image> {
107        let mut c_image: *mut lh::heif_image = ptr::null_mut();
108        let err = unsafe {
109            lh::heif_decode_image(
110                image_handle.inner,
111                &mut c_image,
112                color_space.heif_color_space(),
113                color_space.heif_chroma(),
114                get_decoding_options_ptr(&decoding_options),
115            )
116        };
117        HeifError::from_heif_error(err)?;
118        Ok(Image::from_heif_image(c_image))
119    }
120
121    /// Get a list of available decoders.
122    /// You can filter the decoders by compression format.
123    ///
124    /// The returned list of decoders is sorted by their priority
125    /// (which is a plugin property).
126    pub fn decoder_descriptors(
127        &self,
128        max_count: usize,
129        format_filter: Option<CompressionFormat>,
130    ) -> Vec<DecoderDescriptor<'_>> {
131        let format_filter = format_filter.unwrap_or(CompressionFormat::Undefined);
132        let max_count = max_count.min(libc::c_int::MAX as usize);
133
134        let mut descriptors_ptr = Vec::with_capacity(max_count);
135        unsafe {
136            let count = lh::heif_get_decoder_descriptors(
137                format_filter as _,
138                descriptors_ptr.as_mut_ptr(),
139                max_count as _,
140            );
141            descriptors_ptr.set_len(count as usize);
142        }
143
144        descriptors_ptr
145            .into_iter()
146            .filter_map(|d_ptr| unsafe { d_ptr.as_ref().map(DecoderDescriptor::new) })
147            .collect()
148    }
149
150    /// Get a list of available encoders.
151    /// You can filter the encoders by compression format and name.
152    ///
153    /// The returned list of encoders is sorted by their priority
154    /// (which is a plugin property).
155    ///
156    /// Note: to get the actual encoder from the descriptors returned here,
157    /// use [`LibHeif::encoder`] method.
158    pub fn encoder_descriptors(
159        &self,
160        max_count: usize,
161        format_filter: Option<CompressionFormat>,
162        name_filter: Option<&str>,
163    ) -> Vec<EncoderDescriptor<'_>> {
164        let format_filter = format_filter.unwrap_or(CompressionFormat::Undefined);
165        let max_count = max_count.min(libc::c_int::MAX as usize);
166        let name_filter = name_filter
167            .map(|s| CString::new(s).ok())
168            .unwrap_or_default();
169        let name_filter_ptr = name_filter.map(|s| s.as_ptr()).unwrap_or(ptr::null());
170
171        let mut descriptors_ptr = Vec::with_capacity(max_count);
172        unsafe {
173            let count = lh::heif_get_encoder_descriptors(
174                format_filter as _,
175                name_filter_ptr,
176                descriptors_ptr.as_mut_ptr(),
177                max_count as _,
178            );
179            descriptors_ptr.set_len(count as usize);
180        }
181
182        descriptors_ptr
183            .into_iter()
184            .filter_map(|d_ptr| unsafe { d_ptr.as_ref().map(EncoderDescriptor::new) })
185            .collect()
186    }
187
188    /// Get an encoder instance that can be used to actually
189    /// encode images from a descriptor.
190    pub fn encoder(&self, descriptor: EncoderDescriptor) -> Result<Encoder<'_>> {
191        let mut c_encoder: *mut lh::heif_encoder = ptr::null_mut();
192        let err = unsafe {
193            lh::heif_context_get_encoder(ptr::null_mut(), descriptor.inner, &mut c_encoder)
194        };
195        HeifError::from_heif_error(err)?;
196        let encoder = Encoder::new(unsafe { &mut *c_encoder })?;
197        Ok(encoder)
198    }
199
200    /// Get an encoder for the given compression format.
201    /// If there are several encoder plugins for this format,
202    /// the encoder with the highest plugin priority will be returned.
203    pub fn encoder_for_format(&self, format: CompressionFormat) -> Result<Encoder<'_>> {
204        let mut c_encoder: *mut lh::heif_encoder = ptr::null_mut();
205        let err = unsafe {
206            lh::heif_context_get_encoder_for_format(
207                ptr::null_mut() as _,
208                format as _,
209                &mut c_encoder,
210            )
211        };
212        HeifError::from_heif_error(err)?;
213        let encoder = Encoder::new(unsafe { &mut *c_encoder })?;
214        Ok(encoder)
215    }
216}