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}