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
22pub 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 #[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 #[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}