libheif_rs/
context.rs

1#[cfg(feature = "v1_18")]
2use std::num::NonZeroU16;
3use std::os::raw::c_void;
4use std::{ffi, ptr};
5
6use four_cc::FourCC;
7use libheif_sys as lh;
8
9use crate::encoder::get_encoding_options_ptr;
10use crate::reader::{Reader, HEIF_READER};
11use crate::utils::str_to_cstring;
12#[cfg(feature = "v1_19")]
13use crate::SecurityLimits;
14use crate::{
15    Encoder, EncodingOptions, HeifError, HeifErrorCode, HeifErrorSubCode, Image, ImageHandle,
16    ItemId, Result,
17};
18
19#[allow(dead_code)]
20enum Source<'a> {
21    None,
22    File,
23    Memory(&'a [u8]),
24    Reader(Box<Box<dyn Reader>>),
25}
26
27pub struct HeifContext<'a> {
28    pub(crate) inner: *mut lh::heif_context,
29    source: Source<'a>,
30}
31
32impl HeifContext<'static> {
33    /// Create a new empty context.
34    pub fn new() -> Result<HeifContext<'static>> {
35        let ctx = unsafe { lh::heif_context_alloc() };
36        if ctx.is_null() {
37            Err(HeifError {
38                code: HeifErrorCode::ContextCreateFailed,
39                sub_code: HeifErrorSubCode::Unspecified,
40                message: String::from(""),
41            })
42        } else {
43            Ok(HeifContext {
44                inner: ctx,
45                source: Source::None,
46            })
47        }
48    }
49
50    /// Create a new context from file.
51    pub fn read_from_file(name: &str) -> Result<HeifContext<'static>> {
52        let mut context = HeifContext::new()?;
53        context.read_file(name)?;
54        Ok(context)
55    }
56
57    /// Read a HEIF file from a named disk file.
58    pub fn read_file(&mut self, name: &str) -> Result<()> {
59        self.source = Source::File;
60        let c_name = ffi::CString::new(name).unwrap();
61        let err =
62            unsafe { lh::heif_context_read_from_file(self.inner, c_name.as_ptr(), ptr::null()) };
63        HeifError::from_heif_error(err)?;
64        Ok(())
65    }
66
67    /// Create a new context from reader.
68    pub fn read_from_reader(reader: Box<dyn Reader>) -> Result<HeifContext<'static>> {
69        let mut context = HeifContext::new()?;
70        context.read_reader(reader)?;
71        Ok(context)
72    }
73
74    /// Read a HEIF file from the reader.
75    pub fn read_reader(&mut self, reader: Box<dyn Reader>) -> Result<()> {
76        let mut reader_box = Box::new(reader);
77        let user_data = reader_box.as_mut() as *mut _ as *mut c_void;
78        let err = unsafe {
79            lh::heif_context_read_from_reader(self.inner, &HEIF_READER, user_data, ptr::null())
80        };
81        HeifError::from_heif_error(err)?;
82        self.source = Source::Reader(reader_box);
83        Ok(())
84    }
85
86    /// # Safety
87    ///
88    /// The given pointer must be valid.
89    #[cfg(feature = "v1_18")]
90    pub(crate) unsafe fn from_ptr(ctx: *mut lh::heif_context) -> HeifContext<'static> {
91        HeifContext {
92            inner: ctx,
93            source: Source::None,
94        }
95    }
96}
97
98impl<'a> HeifContext<'a> {
99    /// Create a new context from bytes.
100    ///
101    /// The provided memory buffer is not copied.
102    /// That means, you will have to keep the memory buffer alive as
103    /// long as you use the context.
104    pub fn read_from_bytes(bytes: &[u8]) -> Result<HeifContext> {
105        let mut context = HeifContext::new()?;
106        context.read_bytes(bytes)?;
107        Ok(context)
108    }
109
110    /// Read a HEIF file from bytes.
111    ///
112    /// The provided memory buffer is not copied.
113    /// That means, you will have to keep the memory buffer alive as
114    /// long as you use the context.
115    pub fn read_bytes<'b: 'a>(&mut self, bytes: &'b [u8]) -> Result<()> {
116        self.source = Source::Memory(bytes);
117        let err = unsafe {
118            lh::heif_context_read_from_memory_without_copy(
119                self.inner,
120                bytes.as_ptr() as _,
121                bytes.len(),
122                ptr::null(),
123            )
124        };
125        HeifError::from_heif_error(err)?;
126        Ok(())
127    }
128
129    unsafe extern "C" fn vector_writer(
130        _ctx: *mut lh::heif_context,
131        data: *const c_void,
132        size: usize,
133        user_data: *mut c_void,
134    ) -> lh::heif_error {
135        let vec: &mut Vec<u8> = &mut *(user_data as *mut Vec<u8>);
136        vec.reserve(size);
137        ptr::copy_nonoverlapping::<u8>(data as _, vec.as_mut_ptr(), size);
138        vec.set_len(size);
139
140        lh::heif_error {
141            code: lh::heif_error_code_heif_error_Ok,
142            subcode: lh::heif_suberror_code_heif_suberror_Unspecified,
143            message: b"\0".as_ptr() as _,
144        }
145    }
146
147    pub fn write_to_bytes(&self) -> Result<Vec<u8>> {
148        let mut res = Vec::<u8>::new();
149        let pointer_to_res = &mut res as *mut _ as *mut c_void;
150
151        let mut writer = lh::heif_writer {
152            writer_api_version: 1,
153            write: Some(Self::vector_writer),
154        };
155
156        let err = unsafe { lh::heif_context_write(self.inner, &mut writer, pointer_to_res) };
157        HeifError::from_heif_error(err)?;
158        Ok(res)
159    }
160
161    pub fn write_to_file(&self, name: &str) -> Result<()> {
162        let c_name = ffi::CString::new(name).unwrap();
163        let err = unsafe { lh::heif_context_write_to_file(self.inner, c_name.as_ptr()) };
164        HeifError::from_heif_error(err)
165    }
166
167    pub fn number_of_top_level_images(&self) -> usize {
168        unsafe { lh::heif_context_get_number_of_top_level_images(self.inner) as _ }
169    }
170
171    pub fn top_level_image_ids(&self, item_ids: &mut [ItemId]) -> usize {
172        if item_ids.is_empty() {
173            0
174        } else {
175            unsafe {
176                lh::heif_context_get_list_of_top_level_image_IDs(
177                    self.inner,
178                    item_ids.as_mut_ptr(),
179                    item_ids.len() as _,
180                ) as usize
181            }
182        }
183    }
184
185    pub fn image_handle(&self, item_id: ItemId) -> Result<ImageHandle> {
186        let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
187        let err = unsafe { lh::heif_context_get_image_handle(self.inner, item_id, &mut handle) };
188        HeifError::from_heif_error(err)?;
189        Ok(ImageHandle::new(handle))
190    }
191
192    pub fn primary_image_handle(&self) -> Result<ImageHandle> {
193        let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
194        let err = unsafe { lh::heif_context_get_primary_image_handle(self.inner, &mut handle) };
195        HeifError::from_heif_error(err)?;
196        Ok(ImageHandle::new(handle))
197    }
198
199    pub fn top_level_image_handles(&self) -> Vec<ImageHandle> {
200        let max_count = self.number_of_top_level_images();
201        let mut item_ids = Vec::with_capacity(max_count);
202        unsafe {
203            let count = lh::heif_context_get_list_of_top_level_image_IDs(
204                self.inner,
205                item_ids.as_mut_ptr(),
206                max_count as _,
207            ) as usize;
208            item_ids.set_len(count);
209        }
210        let mut handles = Vec::with_capacity(item_ids.len());
211        for item_id in item_ids {
212            if let Ok(handle) = self.image_handle(item_id) {
213                handles.push(handle);
214            }
215        }
216        handles
217    }
218
219    /// Compress the input image.
220    /// The first image added to the context is also automatically set as the primary image, but
221    /// you can change the primary image later with [`HeifContext::set_primary_image`] method.
222    pub fn encode_image(
223        &mut self,
224        image: &Image,
225        encoder: &mut Encoder,
226        encoding_options: Option<EncodingOptions>,
227    ) -> Result<ImageHandle> {
228        let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
229        unsafe {
230            let err = lh::heif_context_encode_image(
231                self.inner,
232                image.inner,
233                encoder.inner,
234                get_encoding_options_ptr(&encoding_options),
235                &mut handle,
236            );
237            HeifError::from_heif_error(err)?;
238        }
239        Ok(ImageHandle::new(handle))
240    }
241
242    /// Encode the `image` as a scaled down thumbnail image.
243    /// The image is scaled down to fit into a square area of width `bbox_size`.
244    /// If the input image is already so small that it fits into this bounding
245    /// box, no thumbnail image is encoded and `Ok(None)` is returned.
246    /// No error is returned in this case.
247    ///
248    /// The encoded thumbnail is automatically assigned to the
249    /// `master_image_handle`. Hence, you do not have to call
250    /// [`HeifContext::assign_thumbnail()`] method.
251    pub fn encode_thumbnail(
252        &mut self,
253        image: &Image,
254        master_image_handle: &ImageHandle,
255        bbox_size: u32,
256        encoder: &mut Encoder,
257        encoding_options: Option<EncodingOptions>,
258    ) -> Result<Option<ImageHandle>> {
259        let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
260        unsafe {
261            let err = lh::heif_context_encode_thumbnail(
262                self.inner,
263                image.inner,
264                master_image_handle.inner,
265                encoder.inner,
266                get_encoding_options_ptr(&encoding_options),
267                bbox_size.min(i32::MAX as _) as _,
268                &mut handle,
269            );
270            HeifError::from_heif_error(err)?;
271        }
272        Ok(Some(ImageHandle::new(handle)))
273    }
274
275    /// Encodes an array of images into a grid.
276    ///
277    /// # Arguments
278    ///
279    /// * `tiles` - User allocated array of images that will form the grid.
280    /// * `rows` - The number of rows in the grid. The number of columns will
281    ///   be calculated from the size of `tiles`.
282    /// * `encoder` - Defines the encoder to use.
283    ///   See [LibHeif::encoder_for_format()](crate::LibHeif::encoder_for_format).
284    /// * `encoding_options` - Optional, may be None.
285    ///
286    /// Returns an error if `tiles` slice is empty.
287    #[cfg(feature = "v1_18")]
288    pub fn encode_grid(
289        &mut self,
290        tiles: &[Image],
291        rows: NonZeroU16,
292        encoder: &mut Encoder,
293        encoding_options: Option<EncodingOptions>,
294    ) -> Result<Option<ImageHandle>> {
295        let mut handle: *mut lh::heif_image_handle = ptr::null_mut();
296        let mut tiles_inners: Vec<*mut lh::heif_image> =
297            tiles.iter().map(|img| img.inner).collect();
298        let rows = rows.get();
299        let columns = (tiles_inners.len() as u32 / rows as u32).min(u16::MAX as _) as u16;
300        unsafe {
301            let err = lh::heif_context_encode_grid(
302                self.inner,
303                tiles_inners.as_mut_ptr(),
304                rows,
305                columns,
306                encoder.inner,
307                get_encoding_options_ptr(&encoding_options),
308                &mut handle,
309            );
310            HeifError::from_heif_error(err)?;
311        }
312        Ok(Some(ImageHandle::new(handle)))
313    }
314
315    /// Assign `master_image_handle` as the thumbnail image of `thumbnail_image_handle`.
316    pub fn assign_thumbnail(
317        &mut self,
318        master_image_handle: &ImageHandle,
319        thumbnail_image_handle: &ImageHandle,
320    ) -> Result<()> {
321        unsafe {
322            let err = lh::heif_context_assign_thumbnail(
323                self.inner,
324                master_image_handle.inner,
325                thumbnail_image_handle.inner,
326            );
327            HeifError::from_heif_error(err)
328        }
329    }
330
331    pub fn set_primary_image(&mut self, image_handle: &mut ImageHandle) -> Result<()> {
332        unsafe {
333            let err = lh::heif_context_set_primary_image(self.inner, image_handle.inner);
334            HeifError::from_heif_error(err)
335        }
336    }
337
338    /// Add generic, proprietary metadata to an image. You have to specify
339    /// an `item_type` that will identify your metadata. `content_type` can be
340    /// an additional type.
341    ///
342    /// For example, this function can be used to add IPTC metadata
343    /// (IIM stream, not XMP) to an image. Although not standard, we propose
344    /// to store IPTC data with `item_type=b"iptc"` and `content_type=None`.
345    pub fn add_generic_metadata<T>(
346        &mut self,
347        image_handle: &ImageHandle,
348        data: &[u8],
349        item_type: T,
350        content_type: Option<&str>,
351    ) -> Result<()>
352    where
353        T: Into<FourCC>,
354    {
355        let c_item_type = str_to_cstring(&item_type.into().to_string(), "item_type")?;
356        let c_content_type = match content_type {
357            Some(s) => Some(str_to_cstring(s, "content_type")?),
358            None => None,
359        };
360        let c_content_type_ptr = c_content_type.map(|s| s.as_ptr()).unwrap_or(ptr::null());
361        let error = unsafe {
362            lh::heif_context_add_generic_metadata(
363                self.inner,
364                image_handle.inner,
365                data.as_ptr() as _,
366                data.len() as _,
367                c_item_type.as_ptr(),
368                c_content_type_ptr,
369            )
370        };
371        HeifError::from_heif_error(error)
372    }
373
374    /// Add EXIF metadata to an image.
375    pub fn add_exif_metadata(&mut self, master_image: &ImageHandle, data: &[u8]) -> Result<()> {
376        let error = unsafe {
377            lh::heif_context_add_exif_metadata(
378                self.inner,
379                master_image.inner,
380                data.as_ptr() as _,
381                data.len() as _,
382            )
383        };
384        HeifError::from_heif_error(error)
385    }
386
387    /// Add XMP metadata to an image.
388    pub fn add_xmp_metadata(&mut self, master_image: &ImageHandle, data: &[u8]) -> Result<()> {
389        let error = unsafe {
390            lh::heif_context_add_XMP_metadata(
391                self.inner,
392                master_image.inner,
393                data.as_ptr() as _,
394                data.len() as _,
395            )
396        };
397        HeifError::from_heif_error(error)
398    }
399
400    /// If the maximum threads number is set to 0, the image tiles are
401    /// decoded in the main thread. This is different from setting it to 1,
402    /// which will generate a single background thread to decode the tiles.
403    ///
404    /// Note that this setting only affects `libheif` itself. The codecs itself
405    /// may still use multi-threaded decoding. You can use it, for example,
406    /// in cases where you are decoding several images in parallel anyway you
407    /// thus want to minimize parallelism in each decoder.
408    pub fn set_max_decoding_threads(&mut self, max_threads: u32) {
409        let max_threads = max_threads.min(libc::c_int::MAX as u32) as libc::c_int;
410        unsafe { lh::heif_context_set_max_decoding_threads(self.inner, max_threads) };
411    }
412
413    /// Returns the security limits for a context.
414    ///
415    /// By default, the limits are set to the global limits,
416    /// but you can change them with the help of [`HeifContext::set_security_limits()`] method.
417    #[cfg(feature = "v1_19")]
418    pub fn security_limits(&self) -> SecurityLimits {
419        let inner_ptr = unsafe { lh::heif_context_get_security_limits(self.inner) };
420        let inner = ptr::NonNull::new(inner_ptr).unwrap();
421        SecurityLimits::from_inner(inner)
422    }
423
424    /// Overwrites the security limits of a context.
425    #[cfg(feature = "v1_19")]
426    pub fn set_security_limits(&mut self, limits: &SecurityLimits) -> Result<()> {
427        let err = unsafe { lh::heif_context_set_security_limits(self.inner, limits.as_inner()) };
428        HeifError::from_heif_error(err)
429    }
430}
431
432impl Drop for HeifContext<'_> {
433    fn drop(&mut self) {
434        unsafe { lh::heif_context_free(self.inner) };
435    }
436}
437
438unsafe impl Send for HeifContext<'_> {}