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