libheif_rs/
regions.rs

1use libheif_sys as lh;
2use std::ptr;
3
4use crate::utils::get_non_null_ptr;
5use crate::{HeifError, Image, ItemId, Result};
6
7pub struct PointRegion(ptr::NonNull<lh::heif_region>);
8
9impl PointRegion {
10    /// Get the values for a point region.
11    ///  
12    /// This returns the coordinates in the reference coordinate space
13    /// (from the parent region item).
14    pub fn point(&self) -> (i32, i32) {
15        let mut x = 0;
16        let mut y = 0;
17        unsafe {
18            lh::heif_region_get_point(self.0.as_ptr(), &mut x, &mut y);
19        }
20        (x, y)
21    }
22
23    /// Get the transformed values for a point region.
24    ///
25    /// This returns the coordinates in pixels after all transformative
26    /// properties have been applied.
27    pub fn point_transformed(&self, image_id: ItemId) -> (f64, f64) {
28        let mut x = 0.;
29        let mut y = 0.;
30        unsafe {
31            lh::heif_region_get_point_transformed(self.0.as_ptr(), image_id, &mut x, &mut y);
32        }
33        (x, y)
34    }
35}
36
37pub struct RectangleRegion(ptr::NonNull<lh::heif_region>);
38
39/// Rectangle geometry.
40///
41/// The region is represented by a left top corner position,
42/// and a size defined by width and height.
43/// All the interior points and the edge are part of the region.
44#[derive(Default, Copy, Clone, PartialEq, Eq)]
45pub struct Rectangle {
46    pub left: i32,
47    pub top: i32,
48    pub width: u32,
49    pub height: u32,
50}
51
52#[derive(Default, Copy, Clone, PartialEq)]
53pub struct RectangleTransformed {
54    pub left: f64,
55    pub top: f64,
56    pub width: f64,
57    pub height: f64,
58}
59
60impl RectangleRegion {
61    /// Get the values for a rectangle region.
62    ///
63    /// This returns the values in the reference coordinate space
64    /// (from the parent region item).
65    pub fn rectangle(&self) -> Rectangle {
66        let mut res = Rectangle::default();
67        unsafe {
68            lh::heif_region_get_rectangle(
69                self.0.as_ptr(),
70                &mut res.left,
71                &mut res.top,
72                &mut res.width,
73                &mut res.height,
74            );
75        }
76        res
77    }
78
79    /// Get the transformed values for a rectangle region.
80    ///
81    /// This returns the coordinates in pixels after all transformative
82    /// properties have been applied.
83    pub fn rectangle_transformed(&self, image_id: ItemId) -> RectangleTransformed {
84        let mut res = RectangleTransformed::default();
85        unsafe {
86            lh::heif_region_get_rectangle_transformed(
87                self.0.as_ptr(),
88                image_id,
89                &mut res.left,
90                &mut res.top,
91                &mut res.width,
92                &mut res.height,
93            );
94        }
95        res
96    }
97}
98
99pub struct EllipseRegion(ptr::NonNull<lh::heif_region>);
100
101#[derive(Default, Copy, Clone, PartialEq, Eq)]
102pub struct Ellipse {
103    pub x: i32,
104    pub y: i32,
105    pub x_radius: u32,
106    pub y_radius: u32,
107}
108
109#[derive(Default, Copy, Clone, PartialEq)]
110pub struct EllipseTransformed {
111    pub x: f64,
112    pub y: f64,
113    pub x_radius: f64,
114    pub y_radius: f64,
115}
116
117impl EllipseRegion {
118    /// Get the values for an ellipse region.
119    ///
120    /// This returns the values in the reference coordinate space (from
121    /// the parent region item).
122    /// The ellipse is represented by a center position, and a size defined
123    /// by radii in the X and Y directions. All the interior points and
124    /// the edge are part of the region.
125    pub fn ellipse(&self) -> Ellipse {
126        let mut res = Ellipse::default();
127        unsafe {
128            lh::heif_region_get_ellipse(
129                self.0.as_ptr(),
130                &mut res.x,
131                &mut res.y,
132                &mut res.x_radius,
133                &mut res.y_radius,
134            );
135        }
136        res
137    }
138
139    /// Get the transformed values for an ellipse region.
140    ///
141    /// This returns the coordinates in pixels after all transformative
142    /// properties have been applied.
143    /// The ellipse is represented by a center position, and a size defined
144    /// by radii in the X and Y directions. All the interior points and
145    /// the edge are part of the region.
146    pub fn ellipse_transformed(&self, image_id: ItemId) -> EllipseTransformed {
147        let mut res = EllipseTransformed::default();
148        unsafe {
149            lh::heif_region_get_ellipse_transformed(
150                self.0.as_ptr(),
151                image_id,
152                &mut res.x,
153                &mut res.y,
154                &mut res.x_radius,
155                &mut res.y_radius,
156            );
157        }
158        res
159    }
160}
161
162pub struct PolygonRegion(ptr::NonNull<lh::heif_region>);
163
164impl PolygonRegion {
165    /// Get the points in a polygon region.
166    ///
167    /// This returns the values in the reference coordinate space (from
168    /// the parent region item).
169    ///
170    /// A polygon is a sequence of points that form a closed shape.
171    /// The first point does not need to be repeated as the last point.
172    /// All the interior points and the edge are part of the region.
173    /// The points are returned as pairs of (X, Y) coordinates.
174    pub fn polygon_points(&self) -> Vec<(i32, i32)> {
175        let num_points = unsafe { lh::heif_region_get_polygon_num_points(self.0.as_ptr()) };
176        let size = num_points.max(0) as usize;
177        let mut points: Vec<(i32, i32)> = Vec::with_capacity(size);
178        if size > 0 {
179            unsafe {
180                lh::heif_region_get_polygon_points(self.0.as_ptr(), points.as_mut_ptr() as _);
181                points.set_len(size);
182            }
183        }
184        points
185    }
186
187    /// Get the transformed points in a polygon region.
188    ///
189    /// This returns the coordinates in pixels after all transformative
190    /// properties have been applied.
191    ///
192    /// A polygon is a sequence of points that form a closed shape.
193    /// The first point does not need to be repeated as the last point.
194    /// All the interior points and the edge are part of the region.
195    /// The points are returned as pairs of (X, Y) coordinates.
196    pub fn polygon_points_transformed(&self, image_id: ItemId) -> Vec<(f64, f64)> {
197        let num_points = unsafe { lh::heif_region_get_polygon_num_points(self.0.as_ptr()) };
198        let size = num_points.max(0) as usize;
199        let mut points: Vec<(f64, f64)> = Vec::with_capacity(size);
200        if size > 0 {
201            unsafe {
202                lh::heif_region_get_polygon_points_transformed(
203                    self.0.as_ptr(),
204                    image_id,
205                    points.as_mut_ptr() as _,
206                );
207                points.set_len(size);
208            }
209        }
210        points
211    }
212}
213
214pub struct PolylineRegion(ptr::NonNull<lh::heif_region>);
215
216impl PolylineRegion {
217    /// Get the points in a polyline region.
218    ///
219    /// This returns the values in the reference coordinate space (from
220    /// the parent region item).
221    ///
222    /// A polyline is a sequence of points that does not form a closed shape.
223    /// Even if the polyline is closed, the only points that are part of
224    /// the region are those that intersect (even minimally) a one-pixel line
225    /// drawn along the polyline.
226    /// The points are returned as pairs of (X, Y) coordinates.
227    pub fn polyline_points(&self) -> Vec<(i32, i32)> {
228        let num_points = unsafe { lh::heif_region_get_polyline_num_points(self.0.as_ptr()) };
229        let size = num_points.max(0) as usize;
230        let mut points: Vec<(i32, i32)> = Vec::with_capacity(size);
231        if size > 0 {
232            unsafe {
233                lh::heif_region_get_polyline_points(self.0.as_ptr(), points.as_mut_ptr() as _);
234                points.set_len(size);
235            }
236        }
237        points
238    }
239
240    /// Get the transformed points in a polyline region.
241    ///
242    /// This returns the coordinates in pixels after all transformative
243    /// properties have been applied.
244    ///
245    /// A polyline is a sequence of points that does not form a closed shape.
246    /// Even if the polyline is closed, the only points that are part of the
247    /// region are those that intersect (even minimally) a one-pixel line
248    /// drawn along the polyline.
249    /// The points are returned as pairs of (X, Y) coordinates.
250    pub fn polyline_points_transformed(&self, image_id: ItemId) -> Vec<(f64, f64)> {
251        let num_points = unsafe { lh::heif_region_get_polyline_num_points(self.0.as_ptr()) };
252        let size = num_points.max(0) as usize;
253        let mut points: Vec<(f64, f64)> = Vec::with_capacity(size);
254        if size > 0 {
255            unsafe {
256                lh::heif_region_get_polyline_points_transformed(
257                    self.0.as_ptr(),
258                    image_id,
259                    points.as_mut_ptr() as _,
260                );
261                points.set_len(size);
262            }
263        }
264        points
265    }
266}
267
268pub struct ReferencedMaskRegion(ptr::NonNull<lh::heif_region>);
269
270impl ReferencedMaskRegion {
271    /// Get a referenced item mask region.
272    pub fn referenced_mask(&self) -> (Rectangle, ItemId) {
273        let mut rectangle = Rectangle::default();
274        let mut item_id = 0;
275        unsafe {
276            lh::heif_region_get_referenced_mask_ID(
277                self.0.as_ptr(),
278                &mut rectangle.left,
279                &mut rectangle.top,
280                &mut rectangle.width,
281                &mut rectangle.height,
282                &mut item_id,
283            );
284        }
285        (rectangle, item_id)
286    }
287}
288
289pub struct InlineMaskRegion(ptr::NonNull<lh::heif_region>);
290
291impl InlineMaskRegion {
292    /// Get data for an inline mask region.
293    ///
294    /// This returns the values in the reference coordinate space
295    /// (from the parent region item).
296    /// The mask location is represented by a left top corner position,
297    /// and a size defined by a width and height.
298    ///
299    /// The mask is held as inline data on the region, one bit per pixel,
300    /// the most significant bit first pixel, no padding. If the bit value is
301    /// `1`, the corresponding pixel is part of the region. If the bit value
302    /// is `0`, the corresponding pixel is not part of the region.
303    pub fn inline_mask_data(&self) -> (Rectangle, Vec<u8>) {
304        let mut rectangle = Rectangle::default();
305        let size = unsafe { lh::heif_region_get_inline_mask_data_len(self.0.as_ptr()) };
306        let mut data: Vec<u8> = Vec::with_capacity(size);
307        unsafe {
308            lh::heif_region_get_inline_mask_data(
309                self.0.as_ptr(),
310                &mut rectangle.left,
311                &mut rectangle.top,
312                &mut rectangle.width,
313                &mut rectangle.height,
314                data.as_mut_ptr(),
315            );
316            data.set_len(size);
317        }
318        (rectangle, data)
319    }
320}
321
322#[non_exhaustive]
323pub enum Region {
324    /// Point geometry.
325    ///
326    /// The region is represented by a single point.
327    Point(PointRegion),
328    /// Rectangle geometry.
329    ///
330    /// The region is represented by a top left position,
331    /// and a size defined by a width and height.
332    /// All the interior points and the edge are
333    /// part of the region.
334    Rectangle(RectangleRegion),
335    /// Ellipse geometry.
336    ///
337    /// The region is represented by a center point,
338    /// and radii in the X and Y directions.
339    /// All the interior points and the edge are part of the region.
340    Ellipse(EllipseRegion),
341    /// Polygon geometry.
342    ///
343    /// The region is represented by a sequence of points,
344    /// which is considered implicitly closed.
345    /// All the interior points and the edge are part of the region.
346    Polygon(PolygonRegion),
347    /// Polyline geometry.
348    ///
349    /// The region is represented by a sequence of points, which are not
350    /// considered to form a closed surface. Only the edge is part of
351    /// the region.
352    Polyline(PolylineRegion),
353    /// Referenced mask.
354    ///
355    /// The mask location is represented by a left-top corner position,
356    /// and a size defined by a width and height. The value of each sample
357    /// in that mask identifies whether the corresponding pixel is
358    /// part of the region.
359    ///
360    /// The region geometry is described by the pixels in another image item,
361    /// which has an item reference of type `mask` from the region item to
362    /// the image item containing the mask.
363    ///
364    /// The image item containing the mask is one of:
365    /// - a mask item (see ISO/IEC 23008-12:2022 Section 6.10.2), or a derived
366    ///   image from a mask item;
367    /// - an image item in monochrome format (4:0:0 chroma);
368    /// - an image item in format of color with luma and chroma planes
369    ///   (e.g. 4:2:0).
370    ///
371    /// If the pixel value is equal to the minimum sample
372    /// value (e.g. 0 for unsigned integer), the pixel is not part of
373    /// the region. If the pixel value is equal to the maximum sample
374    /// value (e.g. 255 for 8 bit unsigned integer), the pixel is part of
375    /// the region. If the pixel value is between the minimum sample value
376    /// and maximum sample value, the pixel value represents
377    /// an (application defined) probability that the pixel is part of
378    /// the region, where higher pixel values correspond to higher
379    /// probability values.
380    ReferencedMask(ReferencedMaskRegion),
381    /// Inline mask.
382    ///
383    /// The region geometry is described by a sequence of bits stored in inline
384    /// in the region, one bit per pixel. If the bit value is `1`, the pixel is
385    /// part of the region. If the bit value is `0`, the pixel is not part of
386    /// the region.
387    InlineMask(InlineMaskRegion),
388}
389
390impl Region {
391    fn new(region_ptr: ptr::NonNull<lh::heif_region>) -> Option<Self> {
392        let region_type = unsafe { lh::heif_region_get_type(region_ptr.as_ref()) };
393        match region_type {
394            lh::heif_region_type_heif_region_type_point => {
395                Some(Region::Point(PointRegion(region_ptr)))
396            }
397            lh::heif_region_type_heif_region_type_rectangle => {
398                Some(Region::Rectangle(RectangleRegion(region_ptr)))
399            }
400            lh::heif_region_type_heif_region_type_ellipse => {
401                Some(Region::Ellipse(EllipseRegion(region_ptr)))
402            }
403            lh::heif_region_type_heif_region_type_polygon => {
404                Some(Region::Polygon(PolygonRegion(region_ptr)))
405            }
406            lh::heif_region_type_heif_region_type_polyline => {
407                Some(Region::Polyline(PolylineRegion(region_ptr)))
408            }
409            lh::heif_region_type_heif_region_type_referenced_mask => {
410                Some(Region::ReferencedMask(ReferencedMaskRegion(region_ptr)))
411            }
412            lh::heif_region_type_heif_region_type_inline_mask => {
413                Some(Region::InlineMask(InlineMaskRegion(region_ptr)))
414            }
415            _ => None,
416        }
417    }
418
419    fn inner(&self) -> *mut lh::heif_region {
420        match self {
421            Region::Point(region) => region.0.as_ptr(),
422            Region::Rectangle(region) => region.0.as_ptr(),
423            Region::Ellipse(region) => region.0.as_ptr(),
424            Region::Polygon(region) => region.0.as_ptr(),
425            Region::Polyline(region) => region.0.as_ptr(),
426            Region::ReferencedMask(region) => region.0.as_ptr(),
427            Region::InlineMask(region) => region.0.as_ptr(),
428        }
429    }
430}
431
432impl Drop for Region {
433    fn drop(&mut self) {
434        unsafe { lh::heif_region_release(self.inner()) }
435    }
436}
437
438/// Region item.
439///
440/// See ISO/IEC 23008-12:2022 Section 6.10 "Region items and region annotations"
441pub struct RegionItem {
442    pub(crate) inner: *mut lh::heif_region_item,
443}
444
445impl Drop for RegionItem {
446    fn drop(&mut self) {
447        unsafe {
448            lh::heif_region_item_release(self.inner);
449        }
450    }
451}
452
453impl RegionItem {
454    pub(crate) fn new(inner: ptr::NonNull<lh::heif_region_item>) -> Self {
455        Self {
456            inner: inner.as_ptr(),
457        }
458    }
459
460    pub fn id(&self) -> ItemId {
461        unsafe { lh::heif_region_item_get_id(self.inner) }
462    }
463
464    /// Get the reference size for a region item.
465    ///
466    /// The reference size specifies the coordinate space used for
467    /// the region items.
468    /// When the reference size does not match the image size, the regions
469    /// need to be scaled to correspond.
470    pub fn reference_size(&self) -> (u32, u32) {
471        let mut width = 0;
472        let mut height = 0;
473        unsafe {
474            lh::heif_region_item_get_reference_size(self.inner, &mut width, &mut height);
475        }
476        (width, height)
477    }
478
479    /// Get the regions that are part of a region item.
480    pub fn regions(&self) -> Vec<Region> {
481        let num_regions = unsafe { lh::heif_region_item_get_number_of_regions(self.inner) };
482        let size = num_regions.max(0) as usize;
483        let mut region_ptrs: Vec<*mut lh::heif_region> = Vec::with_capacity(size);
484        let mut regions: Vec<Region> = Vec::with_capacity(size);
485        if size > 0 {
486            unsafe {
487                lh::heif_region_item_get_list_of_regions(
488                    self.inner,
489                    region_ptrs.as_mut_ptr(),
490                    num_regions,
491                );
492                region_ptrs.set_len(size);
493            }
494            for region_ptr in region_ptrs.into_iter().filter_map(ptr::NonNull::new) {
495                if let Some(region) = Region::new(region_ptr) {
496                    regions.push(region);
497                }
498            }
499        }
500        regions
501    }
502
503    /// Add a point region to the region item.
504    pub fn add_point(&mut self, x: i32, y: i32) -> Result<Region> {
505        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
506        let err =
507            unsafe { lh::heif_region_item_add_region_point(self.inner, x, y, &mut region_ptr) };
508        HeifError::from_heif_error(err)?;
509        Ok(Region::Point(PointRegion(get_non_null_ptr(region_ptr)?)))
510    }
511
512    /// Add a rectangle region to the region item.
513    pub fn add_rectangle(&mut self, rectangle: Rectangle) -> Result<Region> {
514        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
515        let err = unsafe {
516            lh::heif_region_item_add_region_rectangle(
517                self.inner,
518                rectangle.left,
519                rectangle.top,
520                rectangle.width,
521                rectangle.height,
522                &mut region_ptr,
523            )
524        };
525        HeifError::from_heif_error(err)?;
526        Ok(Region::Rectangle(RectangleRegion(get_non_null_ptr(
527            region_ptr,
528        )?)))
529    }
530
531    /// Add an ellipse region to the region item.
532    pub fn add_ellipse(
533        &mut self,
534        center_x: i32,
535        center_y: i32,
536        radius_x: u32,
537        radius_y: u32,
538    ) -> Result<Region> {
539        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
540        let err = unsafe {
541            lh::heif_region_item_add_region_ellipse(
542                self.inner,
543                center_x,
544                center_y,
545                radius_x,
546                radius_y,
547                &mut region_ptr,
548            )
549        };
550        HeifError::from_heif_error(err)?;
551        Ok(Region::Ellipse(EllipseRegion(get_non_null_ptr(
552            region_ptr,
553        )?)))
554    }
555
556    /// Add a polygon region to the region item.
557    ///
558    /// A polygon is a sequence of points that form a closed shape.
559    /// The first point does not need to be repeated as the last point.
560    /// The points are provided as pairs of (X, Y) coordinates.
561    pub fn add_polygon(&mut self, points: &[(i32, i32)]) -> Result<Region> {
562        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
563        let err = unsafe {
564            lh::heif_region_item_add_region_polygon(
565                self.inner,
566                points.as_ptr() as _,
567                points.len() as _,
568                &mut region_ptr,
569            )
570        };
571        HeifError::from_heif_error(err)?;
572        Ok(Region::Polygon(PolygonRegion(get_non_null_ptr(
573            region_ptr,
574        )?)))
575    }
576
577    /// Add a polyline region to the region item.
578    ///
579    /// A polyline is a sequence of points that does not form a closed shape.
580    /// Even if the polyline is closed, the only points that are part of
581    /// the region are those that intersect (even minimally) a one-pixel
582    /// line drawn along the polyline.
583    /// The points are provided as pairs of (X, Y) coordinates.
584    pub fn add_polyline(&mut self, points: &[(i32, i32)]) -> Result<Region> {
585        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
586        let err = unsafe {
587            lh::heif_region_item_add_region_polyline(
588                self.inner,
589                points.as_ptr() as _,
590                points.len() as _,
591                &mut region_ptr,
592            )
593        };
594        HeifError::from_heif_error(err)?;
595        Ok(Region::Polyline(PolylineRegion(get_non_null_ptr(
596            region_ptr,
597        )?)))
598    }
599
600    /// Add a referenced mask region to the region item.
601    pub fn add_referenced_mask(
602        &mut self,
603        rectangle: Rectangle,
604        mask_item_id: ItemId,
605    ) -> Result<Region> {
606        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
607        let err = unsafe {
608            lh::heif_region_item_add_region_referenced_mask(
609                self.inner,
610                rectangle.left,
611                rectangle.top,
612                rectangle.width,
613                rectangle.height,
614                mask_item_id,
615                &mut region_ptr,
616            )
617        };
618        HeifError::from_heif_error(err)?;
619        Ok(Region::ReferencedMask(ReferencedMaskRegion(
620            get_non_null_ptr(region_ptr)?,
621        )))
622    }
623
624    /// Add an inline mask region to the region item.
625    ///
626    /// The region geometry is described by a left top corner position,
627    /// and a size defined by width and height.
628    ///
629    /// The mask is held as inline data on the region, one bit per pixel,
630    /// the most significant bit first pixel, no padding. If the bit value
631    /// is `1`, the corresponding pixel is part of the region. If the bit
632    /// value is `0`, the corresponding pixel is not part of the region.
633    pub fn add_inline_mask_data(
634        &mut self,
635        rectangle: Rectangle,
636        mask_data: &[u8],
637    ) -> Result<Region> {
638        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
639        let err = unsafe {
640            lh::heif_region_item_add_region_inline_mask_data(
641                self.inner,
642                rectangle.left,
643                rectangle.top,
644                rectangle.width,
645                rectangle.height,
646                mask_data.as_ptr(),
647                mask_data.len() as _,
648                &mut region_ptr,
649            )
650        };
651        HeifError::from_heif_error(err)?;
652        Ok(Region::InlineMask(InlineMaskRegion(get_non_null_ptr(
653            region_ptr,
654        )?)))
655    }
656
657    /// Add an inline mask region image to the region item.
658    ///
659    /// The region geometry is described by a left top corner position,
660    /// and a size defined by width and height.
661    ///
662    /// The mask data is held as inline data in the region, one bit per pixel.
663    /// The provided image is converted to inline data, where any pixel with
664    /// a value >= 0x80 becomes part of the mask region. If the image width
665    /// is less than the specified `rectangle.width`, it is expanded
666    /// to match the `rectangle.width` of the region (zero fill on the right).
667    /// If the image height is less than the specified `rectangle.height`,
668    /// it is expanded to match the `rectangle.height` of the region
669    /// (zero fill on the bottom). If the image width or height is greater
670    /// than the `rectangle.width` or `rectangle.height` (correspondingly)
671    /// of the region, the image is cropped.
672    pub fn add_inline_mask(&mut self, rectangle: Rectangle, image: &Image) -> Result<Region> {
673        let mut region_ptr: *mut lh::heif_region = ptr::null_mut();
674        let err = unsafe {
675            lh::heif_region_item_add_region_inline_mask(
676                self.inner,
677                rectangle.left,
678                rectangle.top,
679                rectangle.width,
680                rectangle.height,
681                image.inner,
682                &mut region_ptr,
683            )
684        };
685        HeifError::from_heif_error(err)?;
686        Ok(Region::InlineMask(InlineMaskRegion(get_non_null_ptr(
687            region_ptr,
688        )?)))
689    }
690}