apple_accelerate/
vimage.rs1use crate::error::{Error, Result};
2use crate::ffi;
3use core::marker::PhantomData;
4use core::ptr;
5
6pub mod vimage_flags {
8 pub const NO_FLAGS: u32 = 0;
9 pub const BACKGROUND_COLOR_FILL: u32 = 4;
10 pub const EDGE_EXTEND: u32 = 8;
11 pub const HIGH_QUALITY_RESAMPLING: u32 = 32;
12}
13
14fn pixel_count(value: usize) -> Result<u64> {
15 u64::try_from(value).map_err(|_| Error::OperationFailed("image dimension exceeds u64"))
16}
17
18fn vimage_result(status: ffi::vImage_Error) -> Result<()> {
19 if status == 0 {
20 Ok(())
21 } else {
22 Err(Error::VImageError(status))
23 }
24}
25
26pub struct ImageBuffer<'a> {
28 inner: ffi::vImage_Buffer,
29 _marker: PhantomData<&'a mut [u8]>,
30}
31
32impl<'a> ImageBuffer<'a> {
33 pub fn from_argb8888(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
34 let expected = width
35 .checked_mul(height)
36 .and_then(|pixels| pixels.checked_mul(4))
37 .ok_or(Error::OperationFailed("image dimensions overflowed"))?;
38 if data.len() < expected {
39 return Err(Error::InvalidLength {
40 expected,
41 actual: data.len(),
42 });
43 }
44
45 Ok(Self {
46 inner: ffi::vImage_Buffer {
47 data: data.as_mut_ptr().cast(),
48 height: pixel_count(height)?,
49 width: pixel_count(width)?,
50 row_bytes: width * 4,
51 },
52 _marker: PhantomData,
53 })
54 }
55
56 pub fn from_planar8(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
57 let expected = width
58 .checked_mul(height)
59 .ok_or(Error::OperationFailed("image dimensions overflowed"))?;
60 if data.len() < expected {
61 return Err(Error::InvalidLength {
62 expected,
63 actual: data.len(),
64 });
65 }
66
67 Ok(Self {
68 inner: ffi::vImage_Buffer {
69 data: data.as_mut_ptr().cast(),
70 height: pixel_count(height)?,
71 width: pixel_count(width)?,
72 row_bytes: width,
73 },
74 _marker: PhantomData,
75 })
76 }
77
78 #[must_use]
79 pub const fn as_ptr(&self) -> *const ffi::vImage_Buffer {
80 &self.inner
81 }
82}
83
84pub fn rotate_argb8888(
85 src: &ImageBuffer<'_>,
86 dst: &mut ImageBuffer<'_>,
87 angle_radians: f32,
88 background_color: [u8; 4],
89 flags: u32,
90) -> Result<()> {
91 let status = unsafe {
93 ffi::vImageRotate_ARGB8888(
94 src.as_ptr(),
95 dst.as_ptr(),
96 ptr::null_mut(),
97 angle_radians,
98 background_color.as_ptr(),
99 flags,
100 )
101 };
102 vimage_result(status)
103}
104
105pub fn box_convolve_argb8888(
106 src: &ImageBuffer<'_>,
107 dst: &mut ImageBuffer<'_>,
108 kernel_height: u32,
109 kernel_width: u32,
110 background_color: [u8; 4],
111 flags: u32,
112) -> Result<()> {
113 let status = unsafe {
115 ffi::vImageBoxConvolve_ARGB8888(
116 src.as_ptr(),
117 dst.as_ptr(),
118 ptr::null_mut(),
119 0,
120 0,
121 kernel_height,
122 kernel_width,
123 background_color.as_ptr(),
124 flags,
125 )
126 };
127 vimage_result(status)
128}
129
130pub fn scale_argb8888(src: &ImageBuffer<'_>, dst: &mut ImageBuffer<'_>, flags: u32) -> Result<()> {
131 let status =
133 unsafe { ffi::vImageScale_ARGB8888(src.as_ptr(), dst.as_ptr(), ptr::null_mut(), flags) };
134 vimage_result(status)
135}
136
137pub fn contrast_stretch_planar8(
138 src: &ImageBuffer<'_>,
139 dst: &mut ImageBuffer<'_>,
140 flags: u32,
141) -> Result<()> {
142 let status = unsafe { ffi::vImageContrastStretch_Planar8(src.as_ptr(), dst.as_ptr(), flags) };
144 vimage_result(status)
145}