Skip to main content

xberg_libheif/
image_handle.rs

1use std::ffi::CString;
2use std::mem::MaybeUninit;
3use std::os::raw::c_char;
4use std::ptr;
5
6use four_cc::FourCC;
7use libheif_sys as lh;
8
9use crate::utils::cstr_to_str;
10use crate::{
11    ColorProfileNCLX, ColorProfileRaw, ColorProfileType, ColorSpace, HeifError, HeifErrorCode, HeifErrorSubCode,
12    ImageMetadata, Result,
13};
14
15cfg_if::cfg_if! {
16    if #[cfg(feature = "v1_18")] {
17        use crate::regions::RegionItem;
18        use crate::HeifContext;
19    }
20}
21
22/// Encoded image.
23pub struct ImageHandle {
24    pub(crate) inner: *mut lh::heif_image_handle,
25}
26
27pub type ItemId = lh::heif_item_id;
28
29#[allow(unsafe_code)]
30impl ImageHandle {
31    pub(crate) fn new(handle: *mut lh::heif_image_handle) -> Self {
32        ImageHandle { inner: handle }
33    }
34
35    #[cfg(feature = "v1_18")]
36    #[allow(unsafe_code)]
37    fn context(&self) -> HeifContext<'_> {
38        unsafe { HeifContext::from_ptr(lh::heif_image_handle_get_context(self.inner)) }
39    }
40
41    #[allow(unsafe_code)]
42    pub fn item_id(&self) -> ItemId {
43        unsafe { lh::heif_image_handle_get_item_id(self.inner) }
44    }
45
46    #[allow(unsafe_code)]
47    pub fn width(&self) -> u32 {
48        unsafe { lh::heif_image_handle_get_width(self.inner) as _ }
49    }
50
51    #[allow(unsafe_code)]
52    pub fn height(&self) -> u32 {
53        unsafe { lh::heif_image_handle_get_height(self.inner) as _ }
54    }
55
56    #[allow(unsafe_code)]
57    pub fn has_alpha_channel(&self) -> bool {
58        unsafe { lh::heif_image_handle_has_alpha_channel(self.inner) != 0 }
59    }
60
61    #[allow(unsafe_code)]
62    pub fn is_premultiplied_alpha(&self) -> bool {
63        unsafe { lh::heif_image_handle_is_premultiplied_alpha(self.inner) != 0 }
64    }
65
66    #[allow(unsafe_code)]
67    pub fn is_primary(&self) -> bool {
68        unsafe { lh::heif_image_handle_is_primary_image(self.inner) != 0 }
69    }
70
71    #[allow(unsafe_code)]
72    pub fn luma_bits_per_pixel(&self) -> u8 {
73        unsafe { lh::heif_image_handle_get_luma_bits_per_pixel(self.inner) as _ }
74    }
75
76    #[allow(unsafe_code)]
77    pub fn chroma_bits_per_pixel(&self) -> u8 {
78        unsafe { lh::heif_image_handle_get_chroma_bits_per_pixel(self.inner) as _ }
79    }
80
81    /// Get the image width from the 'ispe' box.
82    #[allow(unsafe_code)]
83    pub fn ispe_width(&self) -> i32 {
84        unsafe { lh::heif_image_handle_get_ispe_width(self.inner) as _ }
85    }
86
87    /// Get the image height from the 'ispe' box.
88    #[allow(unsafe_code)]
89    pub fn ispe_height(&self) -> i32 {
90        unsafe { lh::heif_image_handle_get_ispe_height(self.inner) as _ }
91    }
92
93    #[allow(unsafe_code)]
94    pub fn has_depth_image(&self) -> bool {
95        unsafe { lh::heif_image_handle_has_depth_image(self.inner) != 0 }
96    }
97
98    #[allow(unsafe_code)]
99    pub fn number_of_depth_images(&self) -> i32 {
100        unsafe { lh::heif_image_handle_get_number_of_depth_images(self.inner) }
101    }
102
103    #[allow(unsafe_code)]
104    pub fn depth_image_ids(&self, item_ids: &mut [ItemId]) -> usize {
105        if item_ids.is_empty() {
106            0
107        } else {
108            unsafe {
109                lh::heif_image_handle_get_list_of_depth_image_IDs(
110                    self.inner,
111                    item_ids.as_mut_ptr(),
112                    item_ids.len() as _,
113                ) as usize
114            }
115        }
116    }
117
118    #[allow(unsafe_code)]
119    pub fn depth_image_handle(&self, depth_image_id: ItemId) -> Result<Self> {
120        let mut out_depth_handler = MaybeUninit::<_>::uninit();
121        let err = unsafe {
122            lh::heif_image_handle_get_depth_image_handle(self.inner, depth_image_id, out_depth_handler.as_mut_ptr())
123        };
124        HeifError::from_heif_error(err)?;
125        let out_depth_handler = unsafe { out_depth_handler.assume_init() };
126        Ok(ImageHandle {
127            inner: out_depth_handler,
128        })
129    }
130
131    #[allow(unsafe_code)]
132    pub fn number_of_thumbnails(&self) -> usize {
133        unsafe { lh::heif_image_handle_get_number_of_thumbnails(self.inner) as _ }
134    }
135
136    #[allow(unsafe_code)]
137    pub fn thumbnail_ids(&self, item_ids: &mut [ItemId]) -> usize {
138        if item_ids.is_empty() {
139            0
140        } else {
141            unsafe {
142                lh::heif_image_handle_get_list_of_thumbnail_IDs(self.inner, item_ids.as_mut_ptr(), item_ids.len() as _)
143                    as usize
144            }
145        }
146    }
147
148    #[allow(unsafe_code)]
149    pub fn thumbnail(&self, thumbnail_id: ItemId) -> Result<Self> {
150        let mut out_thumbnail_handler = MaybeUninit::<_>::uninit();
151        let err = unsafe {
152            lh::heif_image_handle_get_thumbnail(self.inner, thumbnail_id, out_thumbnail_handler.as_mut_ptr())
153        };
154        HeifError::from_heif_error(err)?;
155        let out_thumbnail_handler = unsafe { out_thumbnail_handler.assume_init() };
156        Ok(ImageHandle {
157            inner: out_thumbnail_handler,
158        })
159    }
160
161    fn convert_type_filter<T>(type_filter: T) -> Option<CString>
162    where
163        T: Into<FourCC>,
164    {
165        let type_filter = type_filter.into();
166        if type_filter.0.contains(&0) {
167            None
168        } else {
169            CString::new(type_filter.to_string()).ok()
170        }
171    }
172
173    #[allow(unsafe_code)]
174    pub fn number_of_metadata_blocks<T>(&self, type_filter: T) -> i32
175    where
176        T: Into<FourCC>,
177    {
178        let c_type_filter = Self::convert_type_filter(type_filter);
179        let filter_ptr: *const c_char = match &c_type_filter {
180            Some(s) => s.as_ptr(),
181            None => ptr::null(),
182        };
183        unsafe { lh::heif_image_handle_get_number_of_metadata_blocks(self.inner, filter_ptr) }
184    }
185
186    #[allow(unsafe_code)]
187    pub fn metadata_block_ids<T>(&self, item_ids: &mut [ItemId], type_filter: T) -> usize
188    where
189        T: Into<FourCC>,
190    {
191        if item_ids.is_empty() {
192            0
193        } else {
194            let c_type_filter = Self::convert_type_filter(type_filter);
195            let filter_ptr: *const c_char = match &c_type_filter {
196                Some(s) => s.as_ptr(),
197                None => ptr::null(),
198            };
199            unsafe {
200                lh::heif_image_handle_get_list_of_metadata_block_IDs(
201                    self.inner,
202                    filter_ptr,
203                    item_ids.as_mut_ptr(),
204                    item_ids.len() as _,
205                ) as usize
206            }
207        }
208    }
209
210    #[allow(unsafe_code)]
211    pub fn metadata_type(&self, metadata_id: ItemId) -> Option<&str> {
212        let c_type: *const c_char = unsafe { lh::heif_image_handle_get_metadata_type(self.inner, metadata_id) };
213        cstr_to_str(c_type)
214    }
215
216    #[allow(unsafe_code)]
217    pub fn metadata_content_type(&self, metadata_id: ItemId) -> Option<&str> {
218        let c_type = unsafe { lh::heif_image_handle_get_metadata_content_type(self.inner, metadata_id) };
219        cstr_to_str(c_type)
220    }
221
222    #[allow(unsafe_code)]
223    pub fn metadata_size(&self, metadata_id: ItemId) -> usize {
224        unsafe { lh::heif_image_handle_get_metadata_size(self.inner, metadata_id) }
225    }
226
227    #[allow(unsafe_code)]
228    pub fn metadata_item_uri_type(&self, metadata_id: ItemId) -> Option<&str> {
229        let c_type = unsafe { lh::heif_image_handle_get_metadata_item_uri_type(self.inner, metadata_id) };
230        cstr_to_str(c_type)
231    }
232
233    #[allow(unsafe_code)]
234    pub fn metadata(&self, metadata_id: ItemId) -> Result<Vec<u8>> {
235        let size = self.metadata_size(metadata_id);
236        if size == 0 {
237            return Err(HeifError {
238                code: HeifErrorCode::UsageError,
239                sub_code: HeifErrorSubCode::NonExistingItemReferenced,
240                message: "".to_string(),
241            });
242        }
243        let mut result: Vec<u8> = Vec::with_capacity(size);
244        unsafe {
245            let err = lh::heif_image_handle_get_metadata(self.inner, metadata_id, result.as_ptr() as _);
246            HeifError::from_heif_error(err)?;
247            result.set_len(size);
248        }
249        Ok(result)
250    }
251
252    pub fn all_metadata(&self) -> Vec<ImageMetadata> {
253        let count = self.number_of_metadata_blocks(0).max(0) as usize;
254        let mut item_ids = vec![0; count];
255        let count = self.metadata_block_ids(&mut item_ids, 0);
256        let mut result = Vec::with_capacity(count);
257        for item_id in item_ids {
258            if item_id == 0 {
259                continue;
260            }
261            if let Some(item) = self.item_metadata(item_id) {
262                result.push(item);
263            }
264        }
265        result
266    }
267
268    fn item_metadata(&self, item_id: ItemId) -> Option<ImageMetadata> {
269        let item_type = self
270            .metadata_type(item_id)
271            .filter(|t| t.len() == 4)
272            .map(|t| FourCC::from(t.as_bytes()))?;
273        let content_type = self.metadata_content_type(item_id).map(String::from)?;
274        let uri_type = self.metadata_item_uri_type(item_id).map(|s| s.to_string())?;
275        let raw_data = self.metadata(item_id).ok()?;
276        Some(ImageMetadata {
277            item_type,
278            content_type,
279            uri_type,
280            raw_data,
281        })
282    }
283
284    #[allow(unsafe_code)]
285    pub fn preferred_decoding_colorspace(&self) -> Result<ColorSpace> {
286        let mut lh_colorspace = lh::heif_colorspace_heif_colorspace_undefined;
287        let mut lh_chroma = lh::heif_chroma_heif_chroma_undefined;
288        let err = unsafe {
289            lh::heif_image_handle_get_preferred_decoding_colorspace(self.inner, &mut lh_colorspace, &mut lh_chroma)
290        };
291        HeifError::from_heif_error(err)?;
292        Ok(ColorSpace::from_libheif(lh_colorspace, lh_chroma).unwrap_or(ColorSpace::Undefined))
293    }
294
295    #[allow(unsafe_code)]
296    pub fn color_profile_raw(&self) -> Option<ColorProfileRaw> {
297        let size = unsafe { lh::heif_image_handle_get_raw_color_profile_size(self.inner) };
298        if size == 0 {
299            return None;
300        }
301        let mut result: Vec<u8> = Vec::with_capacity(size);
302        let err = unsafe { lh::heif_image_handle_get_raw_color_profile(self.inner, result.as_ptr() as _) };
303        if err.code != 0 {
304            return None;
305        }
306        unsafe {
307            result.set_len(size);
308        }
309        let c_profile_type = unsafe { lh::heif_image_handle_get_color_profile_type(self.inner) };
310        let profile_type = ColorProfileType::from(c_profile_type as u32);
311
312        Some(ColorProfileRaw {
313            typ: profile_type,
314            data: result,
315        })
316    }
317
318    #[allow(unsafe_code)]
319    pub fn color_profile_nclx(&self) -> Option<ColorProfileNCLX> {
320        let mut profile_ptr = MaybeUninit::<_>::uninit();
321        let err = unsafe { lh::heif_image_handle_get_nclx_color_profile(self.inner, profile_ptr.as_mut_ptr()) };
322        if err.code != 0 {
323            return None;
324        }
325        let profile_ptr = unsafe { profile_ptr.assume_init() };
326        if profile_ptr.is_null() {
327            return None;
328        }
329        Some(ColorProfileNCLX { inner: profile_ptr })
330    }
331
332    #[cfg(feature = "v1_18")]
333    #[allow(unsafe_code)]
334    pub fn add_region_item(&mut self, reference_width: u32, reference_height: u32) -> Result<RegionItem> {
335        let mut lh_region_item_ptr: *mut lh::heif_region_item = ptr::null_mut();
336        let err = unsafe {
337            lh::heif_image_handle_add_region_item(
338                self.inner,
339                reference_width,
340                reference_height,
341                &mut lh_region_item_ptr,
342            )
343        };
344        HeifError::from_heif_error(err)?;
345        let item_ptr = ptr::NonNull::new(lh_region_item_ptr).ok_or(HeifError {
346            code: HeifErrorCode::MemoryAllocationError,
347            sub_code: HeifErrorSubCode::Unspecified,
348            message: "".to_string(),
349        })?;
350        Ok(RegionItem::new(item_ptr))
351    }
352
353    #[cfg(feature = "v1_18")]
354    #[allow(unsafe_code)]
355    pub fn region_items(&self) -> Vec<RegionItem> {
356        let num_items = unsafe { lh::heif_image_handle_get_number_of_region_items(self.inner) };
357        let size = num_items.max(0) as usize;
358        let mut item_ids: Vec<ItemId> = Vec::with_capacity(size);
359        let mut items: Vec<RegionItem> = Vec::with_capacity(size);
360        if size > 0 {
361            unsafe {
362                lh::heif_image_handle_get_list_of_region_item_ids(self.inner, item_ids.as_mut_ptr(), num_items);
363                item_ids.set_len(size);
364            }
365            for item_id in item_ids {
366                let mut item_ptr = ptr::null_mut();
367                let err = unsafe { lh::heif_context_get_region_item(self.context().inner, item_id, &mut item_ptr) };
368                if HeifError::from_heif_error(err).is_ok()
369                    && let Some(region_item_ptr) = ptr::NonNull::new(item_ptr)
370                {
371                    items.push(RegionItem::new(region_item_ptr));
372                }
373            }
374        }
375        items
376    }
377
378    #[allow(unsafe_code)]
379    pub fn auxiliary_images<T: Into<Option<AuxiliaryImagesFilter>>>(&self, filter: T) -> Vec<ImageHandle> {
380        let filter = filter.into().unwrap_or_default();
381        let num_items = unsafe { lh::heif_image_handle_get_number_of_auxiliary_images(self.inner, filter.0) };
382        let size = num_items.max(0) as usize;
383        let mut item_ids: Vec<ItemId> = Vec::with_capacity(size);
384        let mut image_handles = Vec::with_capacity(size);
385        if size > 0 {
386            unsafe {
387                let real_size = lh::heif_image_handle_get_list_of_auxiliary_image_IDs(
388                    self.inner,
389                    filter.0,
390                    item_ids.as_mut_ptr(),
391                    num_items,
392                );
393                item_ids.set_len(real_size as usize);
394            }
395            for item_id in item_ids {
396                let mut handle_ptr = ptr::null_mut();
397                let err =
398                    unsafe { lh::heif_image_handle_get_auxiliary_image_handle(self.inner, item_id, &mut handle_ptr) };
399                if HeifError::from_heif_error(err).is_ok() && !handle_ptr.is_null() {
400                    image_handles.push(ImageHandle::new(handle_ptr));
401                }
402            }
403        }
404        image_handles
405    }
406
407    #[allow(unsafe_code)]
408    pub fn auxiliary_type(&self) -> Result<String> {
409        let mut type_str_ptr = ptr::null();
410        let err = unsafe { lh::heif_image_handle_get_auxiliary_type(self.inner, &mut type_str_ptr) };
411        HeifError::from_heif_error(err)?;
412        let res = cstr_to_str(type_str_ptr).unwrap_or("").to_owned();
413        if !type_str_ptr.is_null() {
414            unsafe { lh::heif_image_handle_release_auxiliary_type(self.inner, &mut type_str_ptr) };
415        }
416        Ok(res)
417    }
418}
419
420#[derive(Copy, Clone, Default)]
421pub struct AuxiliaryImagesFilter(libc::c_int);
422
423impl AuxiliaryImagesFilter {
424    const ALPHA_MASK: libc::c_int = 1 << 1;
425    const DEPTH_MASK: libc::c_int = 2 << 1;
426
427    pub const OMIT_ALPHA: Self = Self::new().omit_alpha();
428    pub const OMIT_DEPTH: Self = Self::new().omit_depth();
429
430    pub const fn new() -> Self {
431        Self(0)
432    }
433
434    pub const fn is_omit_alpha(&self) -> bool {
435        (self.0 & Self::ALPHA_MASK) > 0
436    }
437
438    pub const fn omit_alpha(self) -> Self {
439        Self(self.0 | Self::ALPHA_MASK)
440    }
441
442    pub const fn is_omit_depth(&self) -> bool {
443        (self.0 & Self::DEPTH_MASK) > 0
444    }
445
446    pub const fn omit_depth(self) -> Self {
447        Self(self.0 | Self::DEPTH_MASK)
448    }
449}
450
451#[allow(unsafe_code)]
452impl Drop for ImageHandle {
453    fn drop(&mut self) {
454        unsafe { lh::heif_image_handle_release(self.inner) };
455    }
456}